Use each() on an array in Perl 5.12.

Before Perl 5.12, each only took a hash argument. In list context, it returns a two item list of a key-value pair you had not seen yet (unless you changed the hash in some way that re-ordered it):


while( my( $key, $value ) = each %hash ) {
     ...
     }

This was quite useful for iterating through large hashes, especially those tied to on-disk databases, so you didn’t have to develop the entire list of keys all at once. It lazily-loaded stuff as you asked for it.

Perl 5.12 now lets each do the same thing for arrays. The pair you get back is the index and the value:

while( my( $index, $value ) = each @array ) {
     ...
     }

For the in-memory array, there’s no new functionality here, but it saves you some typing over the old idioms of iterating over the indices:

foreach my $index ( 0 .. $#array ) {
     my $value = $array[$index];
     ...
     }

for( my $i = 0; $i < @array; $i++ ) {
     my $value = $array[$index];
     ...
     }

my $i;
while( $i++ < @array; ) {
     my $value = $array[$index];
     ...
     }

There are some potential benefits for tied arrays, including those tied to on-disk storage, or even (almost) infinite arrays and cyclic arrays. Those previous idioms assume that the array size is fixed and known, but since each doesn't depend explicitly on the array size, so you might think you'd be able to do more tricky things with arrays. You can make you're own tied array that stores nothing and computes values on demand, for instance:

use 5.012;

{
package Tie::Array::InfiniteSquares;

    use parent qw(Tie::Array);
	use Carp qw(carp);
	use Config qw(%Config);

    # mandatory methods
    sub TIEARRAY {
    	bless {}, $_[0];
    	}
    sub FETCH {
    	my( $self, $index ) = @_;
    	$index ** 2;
    	}

    sub FETCHSIZE { 0x7F_FF_FF_FF } # still problems here

    sub STORE     { carp "You can't touch this!" }
    sub STORESIZE { carp "You can't touch this!" }
    sub EXISTS    { 1 }
    sub DELETE    { carp "You can't touch this!" }

}

tie my @array, 'Tie::Array::InfiniteSquares';

while( my( $index, $value ) = each @array )
	{
	say "Item is $value at index $index";
	}

So far, the only problem that the tied approach has here is that FETCHSIZE apparently has to return a number within your perl's built-in limit to the maximum array index, which is 0x7F_FF_FF_FF. If you use 'Inf' or something very big (perhaps with bignum), then you get an error:

FETCHSIZE returned a negative value

If you look in av.c in the perl source (see a code-walking example in Know how Perl handles scientific notation in string to number conversions.), you find that the maximum index is defined as I32, which you find in uconfig defined as the native (signed) long type. This means that Perl doesn't know what to do with an array index past that bound. Oh well, you can't have everything.

This new each functionality only works with arrays. This would be interesting with array slices where you can't guess the next index based on the previous one, but nope, it doesn't:

while( my( $index, $value ) = @array[@indices] ) {  # doesn't work
    ...
    }

Post to Twitter Post to Delicious Post to Digg Post to Facebook Post to Reddit

Leave a comment

0 Comments.

Leave a Reply

You must be logged in to post a comment.