执行perl程序我得到了不同的结果:
$ perl -e 'my $i = 2; $i += 2 + $i++; print "$i\n"'
7
$ perl -e 'my $i = 2; $i += $i + $i++; print "$i\n"'
8
为什么结果会有所不同?在第二种情况下我错过了什么?我希望在这两种情况下7
。
答案 0 :(得分:6)
Perl不保证您发布的任何一个片段的任何特定结果,您应该避免使用此类代码。
也就是说,Perl的所有现有版本的行为都是一致的。
虽然没有记录或保证,Perl总是在右手边(RHS)之前评估加法运算符的左侧(LHS)。 [1]
$ perl -MO=Concise,-exec -e 'my $i = 2; $i += $i + $i++; print "$i\n"'
...
8 <0> padsv[$i:1,2] s > LHS
9 <0> padsv[$i:1,2] sRM \ RHS
a <1> postinc[t2] sK/1 /
b <2> add[t3] sK/2
...
那为什么看起来不是这样呢?
理解发生了什么的关键是Perl堆栈只包含标量(SV*
,包括AV*
等子类型。这意味着$i
将与$i
关联的实际标量放在堆栈上,而不仅仅是值2
。 [2]
这意味着即使$i
在评估$i++
之前被评估并放置在堆栈上并放置在堆栈上,添加运算符也会使用$i
的更新值
$i stack
-- -----
2
$i
2 $i
$i
2 $i,$i
postinc
3 $i,2
add
3 5
如果您愿意,可以通过复制Perl解释器来跟踪发生的事情:
use Data::Alias qw( alias );
my @stack;
my @pad;
sub padsv {
my $padidx = shift;
alias push @stack, $pad[$padidx];
}
sub postinc {
alias my $sv = pop(@stack);
alias my $result = $sv++;
alias push @stack, $result;
}
sub add {
alias my ($lhs, $rhs) = splice(@stack, -2);
alias my $result = $lhs + $rhs;
alias push @stack, $result;
}
{ my $i = 2; say $i + $i++; } # 5
$pad[0] = 2;
padsv(0); padsv(0); postinc(); add();
say pop(@stack); # 5
{ my $i = 1; say $i + $i++ + $i++; } # 5
$pad[0] = 1;
padsv(0); padsv(0); postinc(); add(); padsv(0); postinc(); add();
say pop(@stack); # 5
替代:
sub postinc :lvalue { $_[0]++ }
sub add :lvalue { $_[0] + $_[1] }
{ my $i = 2; say $i + $i++; } # 5
{ my $i = 2; say add($i, postinc($i)); } # 5
{ my $i = 1; say $i + $i++ + $i++; } # 5
{ my $i = 1; say add(add($i, postinc($i)), postinc($i)); } # 5
请注意,这与earlier answer相矛盾。它对发生的事情的解释是完全错误的,并且$i + $i++ + $i++
已经证明了这一点。
这是出于性能原因而完成的。制作标量的副本是昂贵的,并且对堆放的每个标量这样做会对性能产生严重的负面影响。
答案 1 :(得分:3)
首先完成自动增量,$i + $i++
等于3 + 2
如果语义不清楚,你不应该使用这样的表达式。您应该将计算分成多个语句。