At first glance, you'd think the first loop would print out a few elements, and then exit. Then the second loop would start over processing the hash, and do the same. However, the output from this is:#!/usr/bin/perl use strict; use warnings; my %data = ( 'baz' => 'zab', 'bar' => 'rab', 'foo' => 'oof', 'key' => 'yek', 'drop_out' => '- do a last -', ); while (my ($key, $val) = each(%data)) { last if ($key eq 'drop_out'); print("$key => $val\n"); } print("-- Exited the loop. Let's try it again...\n"); while (my ($key, $val) = each(%data)) { last if ($key eq 'die'); print("$key => $val\n"); } print("-- Done\n"); exit(0);
bar => rab
baz => zab
-- Exited the loop. Let's try it again...
foo => oof
key => yek
-- Done
"Holy nacreous arthropod, Batman! Is that some sort of bug in Perl?"
"Well, it certainly has a maleficent odor about it, Boy Wonder, but let's RTFM first."
From perlfunc under each we read:
There is a single iterator for each hash, shared by all "each", "keys", and "values" function calls in the program; it can be reset by reading all the elements from the hash, or by evaluating "keys HASH" or "values HASH".The key points are that there is a single iterator, and that it is (only) reset after all the elements have been read. The anomaly is that the iterator is not reset when you leave the scope containing the each()!
This can lead to bugs, as the following illustrates:
The above produces the following output:#!/usr/bin/perl use strict; use warnings; my %data = ( 'key' => 'yek', 'foo' => 'oof', 'bar' => 'rab', 'baz' => 'zab', 'die' => '- DOOM -', ); eval { do_it(\%data); }; print("-- Opps, I died! Try again...\n"); do_it(\%data); exit(0); ### sub do_it { my $data = $_[0]; while (my ($key, $val) = each(%{$data})) { die if ($key eq 'die'); print("$key => $val\n"); } print("-- Did it\n"); }
bar => rab
baz => zab
foo => oof
-- Opps, I died! Try again...
key => yek
-- Did it
Lessons Learned
Update: Let me rephrase. I'm not saying it IS a bug. I'm trying to convey that this feature gives me the same feeling that a real bug would. I.e., something to be avoided or worked around. Or as demerphq so aptly puts it below: It has code smell.
Also, I realize that there are venerable monks that might read this, and say to themselves, "Well, I could have told you that." This post was not meant for you. It was meant for the others, like myself, that even though we may have read the each() perlfunc entry, we didn't quite catch the full implications and potential consequences of Perl's single iterator implementation.
In reply to The Anomalous each() by jdhedden
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |