in reply to Re: Re: Starting foreach at an arbitrary position
in thread Starting foreach at an arbitrary position

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.

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

    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.

      MarkM writes

      The array remains referenced within the iterator subroutine. Therefore, the memory being used by the array remains referenced.

      Proving what? It would be somewhat stupid to try and iterate an array that no longer existed.

      MarkM writes:

      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.

      ... but it's just test data.

      MarkM writes:

      Using the range operator for very large ranges, outside of the "for (1 .. 1000000) {" case is extremely expensive.

      ...but it's just test data.

      Obviously, noone would use an array of a million sequential integers and an iterator to generate 5 sequential integers from 500001 to 500005--or indeed any sequential set of integers in a real application. The only purpose of the consruction of the array is as a placeholder for "an array of abitrary data" in the test program.

      Any real application would already have the data that it wished to iterate so it would not have to build it. Therefore, your point has no bearing whatsoever on the use of the iterator the code served to demonstrate. Is that so hard to see?


      Examine what is said, not who speaks.

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