Another reason not to use each()
So I’ve learned my lesson. Do not use each().
Always good to read the blogs…
https://blogs.perl.org/users/rurban/2014/04/do-not-use-each.html
Here’s a fun one that caused perl to go into an infinite loop. I suppose being frugal and not assigning things to variables is not necessarily a good idea.
#!/usr/bin/perl use strict; use warnings; my %thingies = %{{qw/a 1 b 2 c 3/}}; my $c=0; #while (my ($a,$b) = each %thingies ) { while (my ($a,$b) = each %{{qw/a 1 b 2 c 3/}} ) { print "$a $b $c\n"; last if ++$c > 4; # stop the madness }
Uncommenting out the line above where each uses a pre-assigned version of the same data element works fine. Not knowing anything regarding the internals of Perl makes me scratch my head. I’m sure there is a perfectly good explanation for this and maybe it has even been corrected in a more recent version of Perl?
$ perl -v This is perl, v5.10.1 (*) built for x86_64-linux-thread-multi
Follow-up
Reading the PerlMonks helps too!
http://www.perlmonks.org/?node_id=754380
I guess perl does not read minds all of the time. In the tradition of do what I want, not what I wrote I had hoped that perl knew that I only wanted to create the data element once, not each time through the loop. My bad.
Moral: Do not use each() - unless you are aware of all of the gotchas and even them don’t use each.
I think the issue here is that `each` expects a hash argument - and you're passing it a list.
The more usual method of assinging to a hash would be:
# assign a list to a hash variable
my %thingies = qw/a 1 b 2 c 3/;
What you're doing is:
# turn a list into a hash-reference
{qw/a 1 b 2 c 3/}
# dereference hash reference back to a list
%{{qw/a 1 b 2 c 3/}}
You can see that you're passing a list to `each` by using Data::Dumper
use Data::Dumper;
my %x=qw/ a 1 b 2 c 3 /;
# inspect a named hash variable
print Dumper(\%x);
# inspect a list
print Dumper(qw/ a 1 b 2 c 3 /);
# turns out to be a list
print Dumper( %{{qw/ a 1 b 2 c 3 /}} );
outputs:
$VAR1 = {
'c' => '3',
'a' => '1',
'b' => '2'
};
$VAR1 = 'a';
$VAR2 = '1';
$VAR3 = 'b';
$VAR4 = '2';
$VAR5 = 'c';
$VAR6 = '3';
$VAR1 = 'c';
$VAR2 = '3';
$VAR3 = 'a';
$VAR4 = '1';
$VAR5 = 'b';
$VAR6 = '2';
I know that you can pass a hashref to `each` by doing:
while ( my ($k,$v) = each %$hashref ) {...}
which appears to be the same thing - but I think that must be special-cased in the parser, as I can't figure out any way of passing a hash to `each` which isn't assigned to a named hash or hash-ref variable.
fireartist: I’m afraid everything you said is wrong, and your entire analysis is mistaken.
In the best case, it would be redundant with bigfoot’s own analysis, who had already identified the problem correctly in the followup section of the post.
Here’s a thought experiment for what you’re missing: how come
push @foo, $bar
pushes something onto@foo
?Why doesn’t that pass the contents of
@foo
topush
?You’re pretty close… outside the fact that the case isn’t that special. Here’s a little challenge:
(The answer to what’s going on is in
perldoc perlsub
.)I think you missed the point. The dereferencing was so that I could inline a hash for each. It is in fact a hash, not a list.
my %thingies = %{{qw/a 1 b 2 c 3/}};
That line was just to pull it out of the loop. No one would never write that.
The problem is that Perl is recreating the hash each time through the loop...obvious after it is described, but again I assumed an optimization that does not exist in Perl (only create the hash once).