jdhedden's meditation from yesterday highlights one anomalous aspect of each that catches us all out at first, and in my case at least, sometimes second and third. This gets raised here every few months, and that's a good thing as the latest bunch of recruits to PM get warned of the problem.

Whilst the general concensus is that it is only a problem until you know about it, there is another, related but different expression of this anomaly that I have never seen raised here. Hence a second meditation rather than a reply to the first.

If you run the following code, it loops forever.

#! perl -slw use strict; $, = ' '; my %h; @h{ 'a' .. 'z' } = 1 .. 26; print %h; while( my $k = each %h ) { print "$k ", %h; }

Can you see why?


Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

Replies are listed 'Best First'.
Re: The anomalous each()--Part II.
by Zaxo (Archbishop) on Nov 19, 2005 at 08:14 UTC
      No, listifies it. Stringification of a hash is different, happens in scalar context, and is a funny looking string like "2/16".
Re: The anomalous each()--Part II.
by ambrus (Abbot) on Nov 19, 2005 at 14:51 UTC
      Am I correct that this is what you meant by saying each in scalar context?

      What I meant by scalar context is that it is more usual to see each used as

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

      Which puts the call to each into a list context.

      Although using each in a scalar context is a perfectly legitimate thing to do (from perlfunc):

      When called in list context, returns a 2-element list consisting of the key and value for the next element of a hash, so that you can iterate over it. When called in scalar context, returns only the key for the next element in the hash.

      You rarely see this use, and when I discovered this anomoly, that was where I thought the problem lay.

      Also, using the hash in an rvalue resets the iterator only if it's in list context. If you change %h to scalar(%h), the loop does end after iterating through all elements.

      Indeed. When I came across the problem, the code was something like this.

      while( my $key = each %hash ) { ... my %reversed = reverse %h; ... }

      The first thing I did to try and work out what was going on was to add the line

      print "$key: ", %hash;

      That showed me that each was producing the same (first) key over and over. Even when I had commented out everything in the loop (except the debug print line), it still looped forever. Hence the problem *had* to be the loop condition. Didn't it?

      It took a long time to work out that the print line itself was duplicating the situation that was originally caused by using %hash as an rvalue in the line above.

      All in all a rather elusive problem. I guess that the behaviour is covered by the line in the perlfunc that reads:

      There is a single iterator for each hash, shared by all each, keys, and values function calls in the program;

      but I'd have to say that I think that it should be clearer that this includes uses of those functions that are invoked by Perl itself, and only indirectly as a result of what the programmer is doing.

      I can't help but think that Perl could localise the hash before using it internally?


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
Re: The anomalous each()--Part II.
by sauoq (Abbot) on Nov 19, 2005 at 22:46 UTC

    The kind of bug you highlight here is exactly the one that bit me while using each() a long (long) time ago. I had actually forgotten why I started to avoid it entirely. Thanks for the reminder. I've never been bitten by the issue jdhedden brought up in his meditation, but it's another good reason to avoid each() whenever possible.

    -sauoq
    "My two cents aren't worth a dime.";