为什么表达结果不同?

时间:2018-04-01 11:00:38

标签: 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

为什么结果会有所不同?在第二种情况下我错过了什么?我希望在这两种情况下7

2 个答案:

答案 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
  1. 请注意,这与earlier answer相矛盾。它对发生的事情的解释是完全错误的,并且$i + $i++ + $i++已经证明了这一点。

  2. 这是出于性能原因而完成的。制作标量的副本是昂贵的,并且对堆放的每个标量这样做会对性能产生严重的负面影响。

答案 1 :(得分:3)

首先完成自动增量,$i + $i++等于3 + 2

如果语义不清楚,你不应该使用这样的表达式。您应该将计算分成多个语句。