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

Very simple code and very simple question - why this cycle never stops.
#!/usr/bin/perl -w use strict; use warnings; use YAML::Syck; my $hash = { 'k1'=>'v1', 'k2'=>'v2', 'k3'=>'v3', }; # Part 1 - works fine while ( my ($k, $v) = each(%$hash) ) { print "$k, $v\n"; } # Part 2 - unlimited cycle. Why???? while ( my ($k, $v) = each(%$hash) ) { print "$k, $v\n"; DumpFile("test.yml", $hash); }

Replies are listed 'Best First'.
Re: Action on each key/value pair in a hash
by beech (Parson) on Nov 01, 2016 at 02:49 UTC

    Hi,

    Its because hashes have only one internal iterator

    my %hash = 1..4; my $stop = 0; while( my( $k, $v ) = each %hash ){ $stop++; print "# $k $v\n"; while( my( $kk, $vv ) = each %hash ){ print "## $kk $vv\n"; } die if $stop > 2; } __END__ # 1 2 ## 3 4 # 1 2 ## 3 4 # 1 2 ## 3 4 Died at - line 9.

    First loop's each starts the iterator,

    The second loop's each continues the iterator until the end is reached, when the iterator resets

    then the first loop's each starts iterator again

    Iterator can also be reset with keys

    $ perl -le" %f=1..4; while( each %f ){print $g++; } " 0 1 $ perl -le" %f=1..4; while( each %f ){print $g++; keys %f; die if $g > + 5; } " 0 1 2 3 4 5 Died at -e line 1.

    YAML::Syck::DumpFile uses keys/each in order to copy the hash, thus it ends up resetting the internal iterator

    Maybe you want to dump the $v variable instead of the $hash? Or just dump the $hash outside of a loop?

Re: Action on each key/value pair in a hash
by LanX (Saint) on Nov 01, 2016 at 08:12 UTC
    Expanding on what beech said

    From each

    > The iterator is implicitly reset when each has reached the end as just described; it can be explicitly reset by calling keys or values on the hash or array. 

    So your DumpFile is internally reseting that counter, probably by calling each ...

    As a rule of thumb: Try to avoid calling (unknown) code operating on the iterated structure within a while-each loop.²

    That's what I do.

    See also this blog , where Reini is discouraging any use of each

    I wouldn't go that far, but YMMV. :)

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

    Update

    ²) Otherwise a warning should be emitted IMHO...

Re: Action on each key/value pair in a hash
by Marshall (Canon) on Nov 01, 2016 at 05:27 UTC
    I read the post from beech++. Another thing to consider is this slight re-write:
    #!/usr/bin/perl use strict; use warnings; use YAML::Syck; my $hash = { 'k1'=>'v1', 'k2'=>'v2', 'k3'=>'v3', }; # Part 1 - works fine while ( my ($k, $v) = each(%$hash) ) { print "$k, $v\n"; } # Part 3 - use foreach loop instead of while(each) foreach my $k (keys %$hash) { print "$k, $hash->{$k}\n"; DumpFile("test.yml", $hash); } __END__ stdout prints: k3, v3 k2, v2 k1, v1 k3, v3 k2, v2 k1, v1 test.yml file has: --- k1: v1 k2: v2 k3: v3
    This works without "hanging". I think that foreach (keys %$hash){} builds a list that is then iterated over and therefore what DumpFile does with the internal hash iterator doesn't matter. Of course in any event, DumpFile should be moved outside of the loop - no need to create it over and over again.