$a = 10;
$b = (++$a) + (++$a) + (++$a);
print $b;
我得到答案37。 任何人都可以解释这个操作是如何进行的以及结果如何得到37。
根据我的逻辑,应该是36:
(++$a) + (++$a) + (++$a)
11 + 12 + 13 = 36
但我得到答案37
答案 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
操作发生在标签6
和8
。 add
操作发生在标签9
和a
。这告诉我们两个增量都是在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
操作仍然在6
和9
进行,但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
我们在preinc
,8
和a
标签处看到d
。 add
操作发生在b
和e
。也就是说,$a
递增两次,然后将两个$a
加在一起。然后$a
再次递增。然后将$a
添加到结果中。因此输出为(12 + 12) + 13 = 37
。