I was just reading through Phil Crow's Perl Design Patterns, but I had a question about one of the topics he discusses - iterators.
He first shows a (clunky) example of a Java iterator, then goes on to compare that against a foreach loop, iterating over the keys of a hash. However, then he goes on to say:
In Perl 6 it will even be possible to return a list that expands lazily, so the above [code] will be more efficient than it is now. In Perl 5, the keys list is built completely when I call keys. In the future, the keys list will be built on demand, saving memory in most cases, and time in cases where the loop ends early.But doesn't Perl 5 violate the "one at a time" definition? I mean, you're not iterating over one element at a time. You're slurping a list, and THEN iterating over that list. This distinction can be important if, for example, you're retrieving a million rows out of a database. That million-element array is now in memory, instead of being fed to you one element at a time.
I also think he's railing on OO a bit by using Java (and statically typed languages in general), and its overly verbose syntax, as his whipping boy. Consider an equivalent Ruby example...
But doesn't Perl 5 violate the "one at a time" definition? I mean, you're not iterating over one element at a time. You're slurping a list, and THEN iterating over that list.
In this example, keys
is not the iterator; foreach
is. each
is a hash's iterator.
Re:keys is not the iterator
djberg96 on 2003-06-20T23:14:49
Right, I understand that. What I mean is that the keys operation happens first and THEN foreach iterates over ALL the values returned by keys, rather than one at a time.Re:keys is not the iterator
malte on 2003-06-21T11:00:07
I think you are right. Perl 5 really lacks a good syntax for iterating over data structures (that are not lists, arrays). Perhabs the best way is to overload the diamond operator to work on any data structure.
Iteration with
while
in Perl can be nice, but it really only works for data structures that can't include values which Perl considers false. As soon as you get towhile(defined(...))
Perl is clunky, too.Perl 6 will probably get it right, but if we're fair the next version of Java (1.5) will also introduce a foreach loop that does the right thing for collections.
Arrays are special with foreach. They're not slurped, but iterated one by one. You could tie an array to each and iterate over the hash keys without slurping. But then, a simple while loop would be easier
To get a lazy list in Perl 5, simply tie. You can have an infinite list and iterate over it using foreach, as long as you implement the list as an array.
package OneToInf;
# Assumes sequential contiguous access:)
sub TIEARRAY { bless \(my $foo = 0) }
sub FETCHSIZE { ${+shift} + 1 }
sub FETCH { ${+shift}++ }
package main;
tie my @array, 'OneToInf';
foreach (@array) {
print "$_\n";
}