each @array in Perl 5.12
Don’t recall seeing anyone blog about it and in fact the documentation is pretty sparse but from Perl 5.12 each can now iterate over an array like so:
use 5.012; use warnings; my @array = 'a' .. 'h'; while (my ($i, $val) = each @array) { say "$i => $val"; }
each on an array returns two values… the index and the value of the current iteration. Thus we get this output from above code:
0 => a 1 => b 2 => c 3 => d 4 => e 5 => f 6 => g 7 => h
Anyway to save me thinking up a blog post please excuse me while I just regurgitate my Stackoverflow answer I gave earlier today about using each with an array:
==start==
Starting with Perl 5.12, each is now more flexible by also working on arrays:
use 5.012; use warnings; my @tokens = 'a' .. 'z'; while (my ($i, $val) = each @tokens) { if ($val =~ m/[aeiou]/) { ($i, $val) = each @tokens; # get next token after a vowel print $val; } } # => bfjpv
One caveat with each, remember the iterator is global and is not reset if you break out of a loop.
For eg:
while (my ($i, $val) = each @tokens) { print $val; last if $i == 12; } # => abcdefghijklm my ($i, $val) = each @tokens; say "Now at => $val ($i)"; # Now at => n (13)
So use keys or values to manually reset the iterator:
keys @tokens; # resets iterator ($i, $val) = each @tokens; say "Now at => $val ($i)"; # Now at => a (0)
==end==
I’ve just come back from a nice weeks break… so I’m still a lazy git in holiday mode here 🙂
/I3az/
Is there any reason to take a loop iterator out of the loop by default ?
I don’t understand very well this change, I think that makes more sense to “add code” those who want a loop iterator outside the loop, better than to make “add code” for those who reuse semantically the same iterator in loops.
Hi poisonbit,
If you referring to my last two bits of code then I’m only using this as an example for the “caveat”.
/I3az/
Great
So now 5.12 has further extended the awkward action-at-a-distance misfit of ‘each’ to operate on more containers than it used to on 5.10. I can see this being abused far more often, creating lots more bugs where the globalness of it accidentally leaks out, than I can see cases for it being useful.
foreach my $idx ( 0 .. $#arr ) { my $elem = $arr[$idx] } is already neat enough. If you actually wanted to preserve the index from one loop to the next; just remove that ‘my’ start using a C-style for() loop.
each is useful on hashes because you can’t just do that with $key; there is no obvious $key++ operation to walk to the “next” key.
Hi Paul,
Actually I think its quite a useful addition and I much prefer its succinctness over the alternatives. However its good to be wary and I for one would do my best avoid any self abuse it may cause using it 🙂
BTW… using an iterator is my preferred solution. For eg. Array::Iterator mentioned in another answer in that stackoverflow question is something I would recommend.
chars Barry
keys() on an array? Really? How interesting.
And using values() is just as interesting 🙂
Be nice to use something like reset()… but surprisingly its already been taken! (http://perldoc.perl.org/functions/reset.html)