为什么表达结果不同?

Why expression results are different?

执行 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

先做自增,$i + $i++等于3 + 2

你不应该在语义不明确的地方使用这样的表达式。您应该将计算拆分到多个语句中。

Perl 不保证您发布的任何片段的任何特定结果,您应该避免此类代码。


也就是说,行为在所有现有的 Perl 版本中是一致的。

虽然这没有记录或保证,但 Perl 总是先计算加法运算符的 left-hand 侧(左轴),然后再计算它们的 right-hand 侧(右轴)。[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

  1. 请注意,这与 相矛盾。它对发生的事情的解释是完全错误的,并被 $i + $i++ + $i++.

  2. 反驳
  3. 这是出于性能原因。制作标量的副本非常昂贵,并且对堆栈中的每个标量都这样做会对性能产生严重的负面影响。