Reputation: 4451
My problem is simple. I have to print some key-value pairs from a hash. I tried two different methods:
METHOD A
#Fill hash from file ($fh has been defined previously)
while(my $line = <$fh>) {
$counter{$line}++;
}
foreach (my($k, $v) = each %counter){
my ($p1, $p2) = split(/$;/o, $k);
chomp $p2;
if($v > 0){
printf "%s_down_%s %s %d\n", $prefix, $p1, $p2, $v;
printf "%s_up_%s %s %d\n", $prefix, $p2, $p1, $v;
}
}
METHOD B
#Fill hash from file ($fh has been defined previously)
while(my $line = <$fh>) {
$counter{$line}++;
}
foreach $k (keys %counter) {
my ($p1, $p2) = split(/$;/o, $k);
my $v = $counter{$p1,$p2};
chomp $p2;
if($v > 0){
printf "%s_down_%s %s %d\n", $prefix, $p1, $p2, $v;
printf "%s_up_%s %s %d\n", $prefix, $p2, $p1, $v;
}
}
Reading the code, I would say both methods get the same result, but, after executing it, method B, got the right results and method A printed just two elements (4 lines).
After running the method A several times, I can see that the elements printed change each execution.
Anybody knows what is going on here?
Upvotes: 1
Views: 79
Reputation: 66883
The use of foreach
with each
is wrong
perl -wE'%h=(a=>1,b=>2,c=>3); for (($k, $v) = each %h) { say "$k => $v" }'
prints
c => 3 c => 3
With while
instead of foreach
we get the correct behaviour.
That this is wrong can be seen from each (my emphasis)
When called on a hash in list context, returns a 2-element list consisting of the key and value for the next element of a hash.
So each
iterates and since foreach
first generates the whole list (to iterate over) each
is made to iterate internally in a (clearly) ill-defined way; this may exhibit an undefined behavior. We end up with an incorrect list of key-value pair(s) for foreach
to iterate over (formed internally using each
).
Read each
page carefully for further subtleties in use of keys
, values
, and each
.
A more telling example
perl -wE'@h{("a".."f")} = (1..6); for (($k, $v) = each %h) { say "$k => $v" }'
prints on my system
e => 5 e => 5
When run correctly with while
the e => 5
pair is returned first. So we get that pair, twice.
(Still trying to figure out why twice ...)
Upvotes: 2