http://www.perlmonks.org?node_id=754380

johngg has asked for the wisdom of the Perl Monks concerning the following question:

I have a script that seems to hang and I have narrowed the problem down to the point where the code iterates, using each, over a de-referenced hash reference that is returned by a subroutine, i.e. each %{ sub { return { ... } }->( ... ) }. The each call keeps returning the first key/value pair rather than presenting the pairs in turn, thus looping for ever. The hang can be cured by storing the returned reference in an intermediate scalar variable before doing the each but I am curious as to why each is failing in this case. The documentation says:-

... 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. ...

which might imply that keys and values would also have problems in this construct but that is not the case. The following code presents a minimal working example to demonstrate the problem. It first runs the code using the intermediate scalar variable and then repeats the exercise without it. Note that I am forced to bail out of the while( ( $k, $v ) = each %{ ... } ) { ... } using a counter to avoid an infinite loop.

use strict; use warnings; my @keys = qw{ A B C D }; my( $k, $v ); print q{-} x 20, qq{\n}; my $hashref = sub { return { map { $_, shift } @keys } }->( 1 .. 4 ); print qq{ Keys: @{ [ keys %{ $hashref } ] }\n}; print qq{Values: @{ [ values %{ $hashref } ] }\n}; print qq{ Each:\n}; print qq{ $k => $v\n} while ( $k, $v ) = each %{ $hashref }; print q{-} x 20, qq{\n}; print qq{ Keys: @{ [ keys %{ sub { return { map { $_, shift } @keys } }->( 1 .. 4 ) } ] }\n}; print qq{Values: @{ [ values %{ sub { return { map { $_, shift } @keys } }->( 1 .. 4 ) } ] }\n}; print qq{ Each:\n}; my $count = 0; while( ( $k, $v ) = each %{ sub { return { map { $_, shift } @keys } }->( 1 .. 4 ) } ) { last if ++ $count > 5; print qq{ $k => $v\n}; } print q{-} x 20, qq{\n};

The output.

-------------------- Keys: A D C B Values: 1 4 3 2 Each: A => 1 D => 4 C => 3 B => 2 -------------------- Keys: A D C B Values: 1 4 3 2 Each: A => 1 A => 1 A => 1 A => 1 A => 1 --------------------

As you can see, while keys and values produce the expected output when dispensing with the intermediate scalar, each gets nowhere fast. I have tested the code using Perl 5.10.0 under Cygwin and ActiveState Perl 5.8.8 under Windows XP.

The fact that the interpreter accepts the syntax and the each attempts to iterate over the hash and fails where keys and values succeed makes me wonder whether this is, perhaps, a small bugette rather than my code trying to do something outside the bounds of the language. Should each be able to operate directly on the de-referenced return from the subroutine just like keys and values or is my code invalid?

Cheers,

JohnGG

Replies are listed 'Best First'.
Re: while( each ... ) caught in an infinite loop
by BrowserUk (Patriarch) on Mar 31, 2009 at 10:44 UTC

    You are calling the subroutine again each time the while loop iterates, which means you are constructing and returning a new anonymous hash for each iteration. As the hashref returned is to a newly construct hash, its iterator starts from the beginning each time.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      Which, I believe can be constructed from a much smaller example:

      while( my @a = each %{{ a=>'b' }} ) { print "forever\n" }
      .

      -Paul

      Thank you. The "small bugette" is in my brain, obviously. I wonder how it is that I can spend hours on a problem and dismally fail to spot the obvious :-(

      Thanks again.

      JohnGG

        It's not at all intuative. I may never have noticed this myself if it wasn't for reading about it in DBM::Deep.

        -Paul