I was alerted to an anomaly that can occur when using each(), and I thought it might be useful to pass it along.
Consider the following code:
#!/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);
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:
    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:

#!/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"); }
The above produces the following output:
    bar => rab
    baz => zab
    foo => oof
    -- Opps, I died!  Try again...
    key => yek
    -- Did it
Even when we die(), totally exit the scope of the subroutine, and then restart it, the each() is not reset.

Lessons Learned

To me, this is definitely a feature that looks like a bug. So you probably won't be seeing me using each() except in really simple cases where I can be total sure it's safe, or for really huge hashes where keys() would not be suitable.

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.


Remember: There's always one more bug.

In reply to The Anomalous each() by jdhedden

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.