airnet
airnet

Reputation: 2673

perl 101 - iterating hash causes never ending looping

No idea why this script runs infinitely. It looks fine to me.


while (my ($k, $v) = each (%ENV)){
 my @k = keys (%ENV);
 if($k eq $k[0]){
   print "ok";
  }
}

Upvotes: 2

Views: 83

Answers (3)

roland.minner
roland.minner

Reputation: 85

The main question has been answered by others, i would however like to caution against the use of a while-each-loop for iterating through a hash. If you want to process the entire hash, i would recommend against using this construct:

while (my ($k, $v) = each %HASH) {
}

Why? This loop will start at the current position the hash iterator was left in %HASH. If the hash iterator is not at the beginning, then this will only process a part of the hash and not the entire hash. Example:

use strict;
use warnings;

# demonstrate hash iterator behaviour with each():
sub somefunc {
    my ($test_ref, $string) = @_;
    while (my ($k, $v) = each %$test_ref) {
            if ($k eq $string){
                    print "$string found\n";
                    return;
            }
    }
    print "$string not found\n";
}

my %test = (
    foo => 'a',
    bar => 'b'
);

somefunc(\%test, 'foo');
somefunc(\%test, 'foo');

This will print:

foo found
foo not found

I once used a while-each-loop for checking some values in a deeply nested data structures. I then exited the loop using return or last. This left the hash iterator dangling at the last position i was checking. Much later, in a different Module, in a different function i then continued to process the hash using again a while-each-loop, assuming it would process the entire hash. It continued however at the last position of the hash iterator, processing only the remainder of the hash. This was a difficult to find bug.

If you want to use each() to process the entire hash, you should always prefix it with keys %HASH. Example:

keys %HASH; # keys will reset the hash iterator
while (my ($k, $v) = each %HASH) { # will really process the ENTIRE hash
}

Mark Reeds Example above would also always work, since he calls keys %ENV before performing the while-each-loop. Through his use of keys() the hash iterator is reset, and as such the while-each-loop will process the entire hash.

Upvotes: 0

Mark Reed
Mark Reed

Reputation: 95252

Calling keys inside the loop resets the iterator used by each. Don't do that. This should work:

my @k = keys (%ENV);
while (my ($k, $v) = each (%ENV)){
 if($k eq $k[0]){
   print "ok";
  }
}

Upvotes: 9

user507077
user507077

Reputation:

The keys (and also the values) functions reset the iterator used by the each function. As the keys do not change you could retrieve them outside of the loop.

Upvotes: 12

Related Questions