Perl条件(三进制)运算符不执行快捷方式求值



条件(三元)运算符表明三元运算符是if。。。CCD_ 2。我一直这么认为,但最近我有一个逻辑问题。

考虑一下这个简短的调试会话:

DB<1> $s='X'
DB<2>  1 ? $s .= '_' : $s = '_'
DB<3> x $s
0  '_'

因此,如果1为真,则应计算表达式$s .= '_'(而不是$s = '_')。

但为什么$s最后只是'_'呢?

三元条件运算符(?:)的优先级高于赋值运算符(=)(Perl运算符的优先级表可以在perlop的运算符优先级和关联性部分找到)。因此,线

1 ? $s .= '_' : $s = '_'

被Perl解析为

(1 ? ($s .= '_') : $s) = '_'

(您可以通过运行perl -MO=Deparse <your program>自行检查)

还要注意的是,$s .= '_'返回$s,并在末尾添加_,并且这个$s可以被分配给(在技术术语中,它是一个左值)。这记录在perlop:的"分配操作员"部分

与C不同,标量赋值运算符生成有效的左值。修改赋值相当于进行赋值,然后修改分配给的变量。

所以,基本上,你的代码正在进行

($s .= '_') = '_';

相当于

$s .= '_';
$s = '_';

这是运算符优先级的问题。

$ perl -MO=Deparse,-p -e '   
> $s='X';
> $t=1;
> $t ? $s .= '_' : $s = '_';
> print $s'
($s = 'X');
($t = 1);
(($t ? ($s .= '_') : $s) = '_');
print($s);
-e syntax OK

无论三元条件是真还是假,最终$s都被设置为"_"

要做你打算做的事情,你需要添加至少一组括号:

1 ? $s .= '_' : ($s = '_');

条件运算符只计算必要的内容,但存在优先级问题。


首先,条件运算符确实保证使用短路评估,这意味着它只评估必要的内容。

$ perl -M5.010 -e'
sub f { say "f" }
sub g { say "g" }
$ARGV[0] ? f() : g();
' 0
f
$ perl -M5.010 -e'
sub f { say "f" }
sub g { say "g" }
say $ARGV[0] ? f() : g();
' 1
g

对于E1 || E2E1 or E2E1 && E2else0也是如此。它们仅在必要时计算右侧操作数。

$ perl -M5.010 -e'
sub f { say "f"; $ARGV[0] }
sub g { say "g"; $ARGV[1] }
say f() || g();
' 3 4
f
3
$ perl -M5.010 -e'
sub f { say "f"; $ARGV[0] }
sub g { say "g"; $ARGV[1] }
say f() || g();
' 0 4
f
g
4

这就是为什么您可以安全地评估open(...) or die(...)。在没有短路的情况下,它将评估dieopen是否成功。


现在,让我们来解释以下内容:

$s = "X";   1 ? $s .= "_" : $s = "_";         say $s;   # _

这是一个优先级问题。以上相当于

$s = "X";   ( 1 ? ($s .= "_") : $s ) = "_";   say $s;   # _

$s .= "_"返回$s,因此条件运算符返回$s,因此字符串_被分配给$s。如果我们添加parens来获得所需的解析,我们就会得到预期的结果。

$s = "X";   1 ? ($s .= "_") : ($s = "_");     say $s;   # X_

备选方案:

$s = "X";   $s = ( 1 ? $s : "" ) . "_";       say $s;   # X_

最新更新