Perl增量运算符

时间:2017-04-21 15:41:26

标签: perl

$a = 10; 
$b = (++$a) + (++$a) + (++$a);
print $b;

我得到答案37。 任何人都可以解释这个操作是如何进行的以及结果如何得到37。

根据我的逻辑,应该是36:

(++$a) + (++$a) + (++$a)
   11  +    12  +    13  = 36

但我得到答案37

4 个答案:

答案 0 :(得分:9)

Perl正在执行此操作

( ( $a = $a + 1 ) + ( $a = $a + 1 ) ) + ( $a = $a + 1 )

您甚至将++$a放在括号中,以便说它们应该在添加之前首先发生,尽管它们具有更高的优先级

这是因为赋值运算符=返回其第一个操作数,这允许像

这样的操作
(my $x = $y) =~ tr/A-Z/a-z/

如果分配的结果只是$y复制到$x那么tr///会导致无法修改一个常数项或等价项,它对存储在任一变量

中的内容没有影响

这是变量$a,执行如下

  • 执行第一个增量,返回$a
    $a现在是11

  • 执行第二个增量,再次返回$a $a现在是12

  • 执行第一个添加,添加两个增量返回的内容 - $a两个 $a为12,因此$a + $a为24

  • 执行第三个增量,再次返回$a
    $a现在是13

  • 执行第二次添加,添加第一次添加(24)和第三次增量($a)返回的内容 $a为13,因此24 + $a为37

请注意,这不应该依赖。它没有记录在任何地方,只是说我们未定义,并且行为可能会随着Perl的任何发布而改变

答案 1 :(得分:6)

作为暴徒和鲍罗丁答案的补充,如果您考虑操作如何与堆栈交互并认识到preinc返回变量而不是其值,您可以清楚地看到发生了什么。

op | a's value | stack
$a | 10        | $a
++ | 11        | $a
$a | 11        | $a $a
++ | 12        | $a $a
+  | 12        | 24
$a | 12        | 24 $a
++ | 13        | 24 $a
+  | 13        | 37

答案 2 :(得分:5)

正如评论中所指出的,在单个语句中多次更改变量会导致未定义的行为,如in perlop所述。

因此未指定确切的行为,并且可能因版本和实现而异。

至于如何运作,这是一种看待它的方法。由于+是二元运算符,因此在每次操作时,当另一个++执行时,其左侧操作数会参与其中。因此,在每个位置$a获取++,并选择另一个增量作为LHS操作数。

这意味着在每个$a操作中,LHS ++会额外增加(到+)一次。第一个之后的+操作必须累积这些,每个额外的一个额外一个。这里有三个术语,是另一个+3,一次。所以总共有7个增量。

另一个(第四个)术语会产生额外的+4等等

perl -wE'$x=10; $y = ++$x + ++$x + ++$x + ++$x; say $y'  # 4*10 + 2+2+3+4

通过将++$x更改为$x++进行调整很有趣 - 效果取决于位置。

步骤增量

  • 首先$a递增(至11)

  • 在第一次添加时,随着第二个$a递增(到11),第一个获得一个凸起以及一个操作数(到12)

  • 在第二次添加中,第二个$a作为操作数递增(到12)

  • 随着第二个添加,第三个$a被更新,从而从两个添加中加上增量,加上其增量(到13)

上面$a的枚举是指他们在声明中的多个位置出现。

答案 3 :(得分:4)

正如@HåkonHægland指出的那样,在B::Concise下运行此代码,它输出Perl脚本生成的操作码,很有启发性。以下是两个与您提供的示例略有不同的示例:

$ perl -E 'say $b=$a + ((++$a)+(++$a))'
6
$ perl -E 'say $b=($a+(++$a)) + (++$a)'
4

那么这里发生了什么?我们来看看操作码:

$ perl -MO=Concise -E 'say $b=$a+((++$a)+(++$a))'
e  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 47 -e:1) v:%,{,469764096 ->3
d     <@> say vK ->e
3        <0> pushmark s ->4
c        <2> sassign sKS/2 ->d
a           <2> add[t6] sK/2 ->b
-              <1> ex-rv2sv sK/1 ->5
4                 <#> gvsv[*a] s ->5
9              <2> add[t5] sKP/2 ->a
6                 <1> preinc sKP/1 ->7
-                    <1> ex-rv2sv sKRM/1 ->6
5                       <#> gvsv[*a] s ->6
8                 <1> preinc sKP/1 ->9
-                    <1> ex-rv2sv sKRM/1 ->8
7                       <#> gvsv[*a] s ->8
-           <1> ex-rv2sv sKRM*/1 ->c
b              <#> gvsv[*b] s ->c
-e syntax OK

此计划中没有条件。最左侧的列表示此程序中的操作顺序。无论你看到ex-rv2sv令牌,Perl正在读取表达式的值,就像全局标量变量一样。

preinc操作发生在标签68add操作发生在标签9a。这告诉我们两个增量都是在Perl执行添加之前发生的,因此最终的表达式就像2 +(2 + 2)= 6。

在另一个例子中,操作码看起来像

$ perl -MO=Concise -E 'say $b=($a+(++$a)) + (++$a)'
e  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 47 -e:1) v:%,{,469764096 ->3
d     <@> say vK ->e
3        <0> pushmark s ->4
c        <2> sassign sKS/2 ->d
a           <2> add[t6] sK/2 ->b
7              <2> add[t4] sKP/2 ->8
-                 <1> ex-rv2sv sK/1 ->5
4                    <#> gvsv[*a] s ->5
6                 <1> preinc sKP/1 ->7
-                    <1> ex-rv2sv sKRM/1 ->6
5                       <#> gvsv[*a] s ->6
9              <1> preinc sKP/1 ->a
-                 <1> ex-rv2sv sKRM/1 ->9
8                    <#> gvsv[*a] s ->9
-           <1> ex-rv2sv sKRM*/1 ->c
b              <#> gvsv[*b] s ->c
-e syntax OK

现在preinc操作仍然在69进行,但add之后标签7处有$a次操作只增加一次。这使得在最终表达式(1 + 1) + 2 = 4中使用的值。

所以在你的例子中:

$ perl -MO=Concise -E '$a=10;$b=(++$a)+(++$a)+(++$a);say $b'
l  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 47 -e:1) v:%,{,469764096 ->3
5     <2> sassign vKS/2 ->6
3        <$> const[IV 10] s ->4
-        <1> ex-rv2sv sKRM*/1 ->5
4           <#> gvsv[*a] s ->5
6     <;> nextstate(main 47 -e:1) v:%,{,469764096 ->7
g     <2> sassign vKS/2 ->h
e        <2> add[t7] sK/2 ->f
b           <2> add[t5] sK/2 ->c
8              <1> preinc sKP/1 ->9
-                 <1> ex-rv2sv sKRM/1 ->8
7                    <#> gvsv[*a] s ->8
a              <1> preinc sKP/1 ->b
-                 <1> ex-rv2sv sKRM/1 ->a
9                    <#> gvsv[*a] s ->a
d           <1> preinc sKP/1 ->e
-              <1> ex-rv2sv sKRM/1 ->d
c                 <#> gvsv[*a] s ->d
-        <1> ex-rv2sv sKRM*/1 ->g
f           <#> gvsv[*b] s ->g
h     <;> nextstate(main 47 -e:1) v:%,{,469764096 ->i
k     <@> say vK ->l
i        <0> pushmark s ->j
-        <1> ex-rv2sv sK/1 ->k
j           <#> gvsv[*b] s ->k
-e syntax OK

我们在preinc8a标签处看到dadd操作发生在be。也就是说,$a递增两次,然后将两个$a加在一起。然后$a再次递增。然后将$a添加到结果中。因此输出为(12 + 12) + 13 = 37