Perl增量运算符

Perl增量运算符,perl,Perl,我得到了答案。 有人能解释一下这项行动是如何进行的,以及结果是如何得到的吗 按照我的逻辑,应该是36: $a = 10; $b = (++$a) + (++$a) + (++$a); print $b; 但我得到的答案是37,正如注释中所指出的,在一条语句中多次更改变量会导致未定义的行为,如前所述 因此,没有指定确切的行为,并且可能因版本和实现而异 至于它是如何工作的,这里有一种方法可以看到它。由于+是一个二进制运算符,因此在另一个操作上执行+时,在每个操作中都会涉及到其左侧操作数。因此,在

我得到了答案。 有人能解释一下这项行动是如何进行的,以及结果是如何得到的吗

按照我的逻辑,应该是36:

$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++
来调整这一点很有趣——效果取决于位置

逐步递增

  • 第一个
    $a
    递增(到11)

  • 在第一次加法中,当第二个
    $a
    递增(到11)时,第一个得到一个凹凸,同时也是一个操作数(到12)

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

  • 当第二个加法出现时,第三个
    $a
    被更新,从而从两个加法中提取增量,再加上其增量(到13)


上面对
$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

    $a
    现在是11

  • 执行第二个增量,再次返回
    $a

    $a
    现在是12

  • 执行第一个加法,将两个增量返回的值相加,这两个增量都是
    $a

    $a
    是12,所以
    $a+$a
    是24

  • 执行第三个增量,再次返回
    $a

    $a
    现在是13

  • 执行第二个加法,将第一个加法返回的内容(24)和第三个增量(
    $a
    )相加
    $a
    是13,所以
    24+$a
    是37


请注意,这不应该被依赖。除了说它是未定义的之外,其他地方都没有记录,而且行为可能会随着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