bliako:
I took a look at the parsed version of the code (perl -MO=Concise,-exec t.pl), and while I'm not an expert at playing with the perl opcodes, it seems to be building a list of values and then creating the hash. I've annotated it as I understand it, so I'm halfway expecting someone to chime in with a correction. So here's what I think is going on:
# Build the entry for $m
c <0> padsv[$m:1252,1255] vM/LVINTRO
d <;> nextstate(main 1253 t.pl:14) v:*,&,{,x*,x&,x$,$
# Start building %hash
e <0> pushmark s
# First the constant "b"
f <$> const[PV "b"] s
# Set the value of $m to $tests{b} if it exists
g <+> multideref($tests{"b"}) sK/EXISTS
h <|> and(other->i) sK/1
i <+> multideref($tests{"b"}) sK
j <0> padsv[$m:1252,1255] sRM*
k <2> sassign sKS/2
# Now add either $m to the list (if it's defined) or 0 (if not)
l <1> defined sK/1
m <|> cond_expr(other->n) lK/1
n <0> padsv[$m:1252,1255] s # add $m to the list
goto o
20 <$> const[IV 0] s # or add 0 to the list
# Change the value of $m to $tests{a} if it exists
o <$> const[PV "a"] s
p <+> multideref($tests{"a"}) sK/EXISTS
q <|> and(other->r) sK/1
r <+> multideref($tests{"a"}) sK
s <0> padsv[$m:1252,1255] sRM*
t <2> sassign sKS/2
# If $m is defined, add it to the list, otherwise add 0 to the list
u <1> defined sK/1
v <|> cond_expr(other->w) lK/1
w <0> padsv[$m:1252,1255] s
goto x
1z <$> const[IV 0] s
# Finish building the hash
x <0> pushmark s
y <0> padhv[%hash:1253,1255] lRM*/LVINTRO
z <2> aassign[t5] vKS/COM_AGG
Since the list contains references to $m instead of the current value of $m, even though $m *does* get reassigned during your expression, it doesn't matter because it only uses the final value when the hash is built from the list.
To check my hypothesis, I changed your code a bit:
$ cat t.pl
use strict;
use warnings;
use Data::Dumper;
my %tests = (
'a' => 10,
'b' => 20,
'd' => 40,
);
my ($m, $n);
# this seems to assign $m once and never bother to check again
my %hash = (
'd' => exists($tests{'d'}) && defined($n=$tests{'d'}) ? $n : 0,
'b' => exists($tests{'b'}) && defined($m=$tests{'b'}) ? $m : 0,
'c' => exists($tests{'c'}) && defined($m=$tests{'c'}) ? $m : 0,
'a' => exists($tests{'a'}) && defined($m=$tests{'a'}) ? $m : 0,
);
print Dumper(\%hash);
$ perl t.pl
$VAR1 = {
'c' => 0,
'b' => 10,
'd' => 40,
'a' => 10
};
Sure enough, the element for 'd' survived since it used $n instead of $m, while the value for 'b' was clobbered because the value for 'a' also used $m.
Edit: I need to refresh the page periodically, 'cause by the time I finally figured it out and replied, it was all said and done! ;^D
...roboticus
When your only tool is a hammer, all problems look like your thumb.