Eug*_*kov 1 perl operator-precedence
执行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
Run Code Online (Sandbox Code Playgroud)
为什么结果不同?在第二种情况下我错过了什么?我希望7在这两种情况下.
对于您发布的任何一个片段,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
...
Run Code Online (Sandbox Code Playgroud)
那么为什么看起来并非如此呢?
理解发生了什么的关键是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
Run Code Online (Sandbox Code Playgroud)
如果需要,您可以跟踪复制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
Run Code Online (Sandbox Code Playgroud)
替代方案:
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
Run Code Online (Sandbox Code Playgroud)
请注意,这与之前的答案相矛盾.它对发生的事情的解释是完全错误的并且被证明是错误的$i + $i++ + $i++.
这是出于性能原因而完成的.制作标量的副本是昂贵的,并且对堆放的每个标量这样做会对性能产生严重的负面影响.