Perl增量运算符
我得到了答案。 有人能解释一下这项行动是如何进行的,以及结果是如何得到的吗 按照我的逻辑,应该是36:Perl增量运算符,perl,Perl,我得到了答案。 有人能解释一下这项行动是如何进行的,以及结果是如何得到的吗 按照我的逻辑,应该是36: $a = 10; $b = (++$a) + (++$a) + (++$a); print $b; 但我得到的答案是37,正如注释中所指出的,在一条语句中多次更改变量会导致未定义的行为,如前所述 因此,没有指定确切的行为,并且可能因版本和实现而异 至于它是如何工作的,这里有一种方法可以看到它。由于+是一个二进制运算符,因此在另一个操作上执行+时,在每个操作中都会涉及到其左侧操作数。因此,在
$a = 10;
$b = (++$a) + (++$a) + (++$a);
print $b;
但我得到的答案是37,正如注释中所指出的,在一条语句中多次更改变量会导致未定义的行为,如前所述 因此,没有指定确切的行为,并且可能因版本和实现而异 至于它是如何工作的,这里有一种方法可以看到它。由于
+
是一个二进制运算符,因此在另一个操作上执行+
时,在每个操作中都会涉及到其左侧操作数。因此,在每个位置,$a
获取++
ed,并拾取另一个增量作为LHS操作数
这意味着LHS$a
在每个+
操作中额外增加一次(到其++
)。在第一次操作之后的+
操作必须累积这些值,每额外一个术语额外增加一个值。这里有三个术语,这是另一个+3,一次。所以总共有7个增量
还有一个(第四个)术语会导致额外的+4,等等
(++$a) + (++$a) + (++$a)
11 + 12 + 13 = 36
通过将++$x
更改为$x++
来调整这一点很有趣——效果取决于位置
逐步递增
- 第一个
递增(到11)$a
- 在第一次加法中,当第二个
递增(到11)时,第一个得到一个凹凸,同时也是一个操作数(到12)$a
- 在第二次加法中,第二个
作为操作数递增(至12)$a
- 当第二个加法出现时,第三个
被更新,从而从两个加法中提取增量,再加上其增量(到13)$a
上面对
$a
的枚举指的是它们在语句中的多个位置。正如@Håkon Hægland所指出的,在B::concrete
下运行此代码,它输出Perl脚本生成的操作码,这很有启发性。这里有两个与您提供的示例稍有不同的示例:
perl -wE'$x=10; $y = ++$x + ++$x + ++$x + ++$x; say $y' # 4*10 + 2+2+3+4
这是怎么回事?让我们看看操作码:
$ perl -E 'say $b=$a + ((++$a)+(++$a))'
6
$ perl -E 'say $b=($a+(++$a)) + (++$a)'
4
现在,preinc
操作仍然发生在6
和9
处,但是在$a
仅增加一次之后,在标签7
处有一个添加
操作。这使得最终表达式中使用的值(1+1)+2=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
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
$perl-MO=简明-E'$a=10$b=(++$a)+(++$a)+(+++$a);比如说$b'
l离开[1参考]vKP/REFC->(结束)
1输入->2
2下一状态(主47-e:1)v:%,{,469764096->3
5.设计vKS/2->6
3常数[IV 10]s->4
-ex-rv2sv sKRM*/1->5
4 gvsv[*a]s->5
6下一状态(主47-e:1)v:%,{,469764096->7
g设计vKS/2->h
e添加[t7]sK/2->f
b添加[t5]sK/2->c
8预INC sKP/1->9
-ex-rv2sv sKRM/1->8
7 gvsv[*a]s->8
a预INC sKP/1->b
-ex-rv2sv sKRM/1->a
9 gvsv[*a]s->a
d预INC sKP/1->e
-ex-rv2sv sKRM/1->d
c gvsv[*a]s->d
-ex-rv2sv sKRM*/1->g
f gvsv[*b]s->g
下一州(主要47-e:1)v:%,{,469764096->i
k说vK->l
i推动标记s->j
-ex-rv2sv sK/1->k
j gvsv[*b]s->k
-e语法正常
我们看到
preinc
出现在标签8
、a
和d
上。add
操作出现在b
和e
上。也就是说,$a
增加两次,然后两个$a
加在一起。然后$a
再次增加。然后$a
添加到t上因此,输出是(12+12)+13=37
Perl的执行过程如下
$ 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
您甚至将+$a
放在括号中,表示它们应该首先发生在添加之前,尽管它们具有更高的优先级
这是围绕赋值运算符=
返回其第一个操作数这一事实展开的,这允许执行以下操作
( ( $a = $a + 1 ) + ( $a = $a + 1 ) ) + ( $a = $a + 1 )
如果赋值的结果只是从$y
复制到$x
的值,那么tr///
将导致a无法修改常量项或等效项,并且它不会影响存储在任一变量中的内容
下面是变量$a
,执行如下
- 执行第一个增量,返回
$a
现在是11$a
- 执行第二个增量,再次返回
$a
现在是12$a
- 执行第一个加法,将两个增量返回的值相加,这两个增量都是
$a
是12,所以$a
是24$a+$a
- 执行第三个增量,再次返回
$a
现在是13$a
- 执行第二个加法,将第一个加法返回的内容(24)和第三个增量(
)相加$a
是13,所以$a
是3724+$a
请注意,这不应该被依赖。除了说它是未定义的之外,其他地方都没有记录,而且行为可能会随着Perl的任何版本而改变。作为mob和Borodin答案的补充,如果您考虑到操作是如何与堆栈交互的,并且认识到preinc r返回变量,而不是其值
(my $x = $y) =~ tr/A-Z/a-z/
请参阅@ChrisCharley comment中链接的相关部分。请注意,正如在C中一样,Perl没有定义变量何时递增或递减。您只知道它将在返回值之前或之后某个时间执行。这也意味着在sa中修改变量两次
(my $x = $y) =~ tr/A-Z/a-z/
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