Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Re: Starting foreach at an arbitrary position

by BrowserUk (Patriarch)
on Jan 16, 2003 at 21:29 UTC ( [id://227510]=note: print w/replies, xml ) Need Help??


in reply to Starting foreach at an arbitrary position

Another alternative is to use an iterator

sub getIter{ my ($start,$end, $aryRef) = @_; $end=$#$aryRef if $end==-1; return sub { return $aryRef->[$start++] if $start <= $end; (); } } my @a = 1 .. 1000000; my $iter = getIter 500000, 500005, \@a; while ( $iter->() ) { print $_, $/; }

gives 500001 500002 500003 500004 500005 500006 </code>

No long lists created, just discard the iterator when your done with it, have as many active simutaneously as you need and it's a fairly trivial modification to make it move in steps or backwards etc.


Examine what is said, not who speaks.

The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

Replies are listed 'Best First'.
Re: Re: Starting foreach at an arbitrary position
by MarkM (Curate) on Jan 17, 2003 at 06:03 UTC

    Your iterator function has a few problems with it.

    First, the code does not give the output you suggest. To make your code work properly, the while condition should read:

    while (($_) = $iter->()) {

    The () around $_ is necessary as $iter->() returns () when the iterator is done.

    Second, the code does not allow foreach()-like behaviour. $_ is not aliased to the array element value and does not allow expressions such as the following to have effect: (from the original node)

    $_ = uc $_;

    Third, the range operator ('..') still creates a list that is 1000000 scalars long, and copies this list into the array. Try the following code, and watch your machine quickly die:

    $ perl -e 'push(@a, [1 .. 1000000]) while 1'

    (NOTE: The range operator optimization only applies when used directly from a foreach loop. For example: 'for (1 .. 1000000) { ... }')

    All in all, a for(;;) loop with a 'local *_ = \ $array[$i]' is far more effective and efficient... :-)

      Your right about the output. I originally typed the test code into a perl shell like so.

      C:\test>p1 perl> sub getIter{ my ($s,$e,$ref)=@_; $e=$#$ref if $e==-1; return sub + { $s<=$e ? $ref->[$s++] : (); }} perl> @a = 1 .. 1000000 perl> $iter = getIter 500000, 500005, \@a perl> print while $_ = $iter->() 500001500002500003500004500005500006 perl>

      Which, whilst it proved that the basic idea worked, wasn't very palitable. So, being lazy, I C&P'd from the screen and modified it in the edit box. Bad move.

      BTW. You forgot to mention that the condition needs to be "<" not "<=" :^).

      However, making the above code into a normal program like so

      #! perl -slw use strict; sub getIter{ my ($start, $end, $aryRef) = @_; $end = $#$aryRef if $end == -1; return sub { return $aryRef->[$start++] if $start < $end; (); } } my @a = 1 .. 1000000; my $iter = getIter 500000, 500005, \@a; print while $_ = $iter->(); __END__ 500001 500002 500003 500004 500005

      Does produce the output I indicated.

      You can take it a step further step further using *_.

      #! perl -slw use strict; sub getIter{ my ($s, $e, $ref)=@_; $e = $#$ref if $e == -1; return sub { ($s < $e) ? *_ = \$ref->[$s++] : return; } } my @a = 1 .. 1000000; my $iter = getIter 500000, 500005, \@a; print while $iter->(); $iter = getIter 500000, 500005, \@a; $_ = 'modified' while $iter->(); $iter = getIter 500000, 500005, \@a; print while $iter->(); __END__ C:\test>227478 500001 500002 500003 500004 500005 modified modified modified modified modified C:\test>

      Which does alais the array being iterated and allow write operations. However, it stomps on $_ which a for loop doesn't.

      #! perl -slw use strict; $_ = 'some value'; print; for (1 .. 10) { print; } print; __END__ C:\test>temp some value 1 2 3 4 5 6 7 8 9 10 some value C:\test>

      I am unaware of any way of emulating perl's behaviour in localising $_ in your own iterators unfortunately.

      You can wrap the use of the iterator in a bare block and localise $_ for the duration of the scope of use, which is effectively what you are doing, though the for loop block provides a naturally convienient scope rather than having to create one artificially.

      With regard to the other two matters.

      Why

      while ( ($_) = $iter->() ) { ...

      rather than

      while ($_ = $iter-() ) { ...?

      As for the range operator building a list in list assignment. Yes, of course it does, but it is just a convenient way to set up some test data and doesn't having any bearing on the use of the iterator?


      Examine what is said, not who speaks.

      The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

        BrowserUk writes:

        Why while ( ($_) = $iter->() ) { ... rather than while ($_ = $iter->() ) { ...?

        Since your iterator function always returns the current element value, the while loop would terminate prematurely if one of the element values contained a non-truth value (undef, 0, ""). Even if defined() was used, the loop would terminate prematurely on an undef value. Using ($_) causes the list value to be checked for 'truthfullness', meaning that the loop will only terminate when the iterator returns the empty list. This is the desired behaviour, is it not? (Your iterator list does return () when it is complete)

        BrowserUk writes:

        As for the range operator building a list in list assignment. Yes, of course it does, but it is just a convenient way to set up some test data and doesn't having any bearing on the use of the iterator?

        The array remains referenced within the iterator subroutine. Therefore, the memory being used by the array remains referenced. 1000000 scalars, even as simple integers, take up quite a bit of space. I estimate that 1000000 integer scalars take up at least 10 Mbytes of store. Using the range operator for very large ranges, outside of the "for (1 .. 1000000) {" case is extremely expensive.

        UPDATE: I missed the point of the array. For an explanation of the striken out text, see BrowserUk's response.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://227510]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (5)
As of 2024-03-28 14:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found