在Perl中,||运算符是如何工作的?

6

So here are the

$y = 0 | 2 | 4;  # answer is  6

$x = 0 || 2 || 4;  # answer is 2

我知道为什么$y6,因为它在每个数字上使用OR运算符,2 | 4 = 6,但对于$x...

为什么是2


7
请参阅文档:http://perldoc.perl.org/perlop.html#Bitwise-Or-and-Exclusive-Or 与 http://perldoc.perl.org/perlop.html#C-style-Logical-Or,以了解更多信息。 - FMc
4个回答

13

因为2是第一个非假值,逻辑或运算符会停止计算。它会先判断0,发现是假值,然后判断2,发现不是假值,所以直接返回了2。考虑以下示例的输出:

$val = 1;
sub a_proc {
    print "a_proc: ", $val++, "\n";
    1;
}
$another_val = &a_proc || &a_proc;

这将输出a_proc: 1。只要a_proc返回真值,解释器就可以停止评估,因为true和任何值的逻辑或操作结果都是true


实际上,它不是这样处理语句的吗:(0 || 2) || 4?这意味着它实际上进行了两次计算,但短路了第二次。 - TLP
5
@TLP,“短路”指的是不对操作数进行评估。由于有两个二进制运算符,实际上有四个操作数。左侧的两个||被评估,但只有最左侧的||的右侧被评估。1)评估0||2。2)评估0。3)评估2。4)由于“短路”,即左侧的0 || 2返回true,因此不会评估4 - ikegami
@ikegami,我知道什么是短路计算。我的观点是,在一行中使用 || 进行比较时,Perl 不会向前查找并说:“好的,该行的其余部分只有更多的 || 比较,我可以在这里进行短路计算。” 它实际上识别和评估第二个 ||,然后相应地进行短路运算。这样,一个真值就可以“贯穿”整个 || 语句链,但链本身不被短路运算。 - TLP
@TLP - ikegami 是正确的。解释器一旦得到 true 值就停止计算了。我会编辑一个例子来证明这一点。 - D.Shawley
@D.Shawley 是的,我自己也尝试过。我在这里想知道的是当有多个 || 连接在一起时会发生什么。Perl 是否将整个语句链识别为一个并立即短路,还是始终通过它们所有(始终只检查 LHS)。我知道这是一个相当无用的知识,但我很好奇。 - TLP
显示剩余2条评论

8
它是一种短路、保值的逻辑或运算。基本上,它依次评估位于||之间的每个内容,直到找到一个不为假的内容,然后返回该内容并且不评估任何后续操作数。
编辑
Perl逻辑运算符有两个重要特点:
  • 它们是短路的,因为它们首先评估左操作数,如果它为真(对于&&为假),则不评估右操作数。

  • 它们是保值的,因为它们将操作数转换为布尔值来进行合取(and用于确定是否评估右操作数),但表达式的结果是转换为布尔值之前的原始值。

这两个特点都非常重要,并且它们结合起来使得||在Perl中特别有用——比在C/C++中更加实用,那里只是短路而不是保值。

2
如果操作数是函数调用,这一点尤其重要,因为涉及到性能和副作用(或避免副作用)。 - karmakaze
1
逻辑 AND 也会短路,并用于测试变量的定义性。defined $var && doSomethingWith($var) 这可以防止 doSomethingWith() 尝试处理未定义的变量。 - bitbucket

5
虽然这两个运算符看起来相似,但它们的目的实际上是不同的。从perldoc perlop中了解到:
二进制“||”执行短路逻辑或运算。也就是说,如果左操作数为真,则右操作数甚至不会被评估。
相比之下:
二进制“|”将其操作数按位进行逻辑或运算。 || 的目的是得出“x || y 是真还是假”的答案,而按位或 - | - 的目的是得出操作数的乘积(?)或类似于“x | y 的结果是什么?”的东西。
由于 || 的唯一有趣的两个结果是真或假,因此该语句可以(并且是)短路,从而导致这种效果。
在第一个语句中:(0 | 2) = 2, (2 | 4) = 6 在第二个语句中:(0 || 2) = 2, (2 || ...) = 2 有趣的是,按位或 是在二进制数内设置布尔值。将 true 或 false 值添加到数字的二进制表示中的位置。
0000 | 0010 = 0010
0010 | 0100 = 0110
0110 | 0001 = 0111
0111 | 0001 = 0111 # no change

这对于在一个数字中存储多个布尔值非常方便,可以使用& (按位与)进行检查。

0101 & 0100 = 0100 (true)
0101 & 0010 = 0000 (false)

有十种人:懂二进制的和不懂二进制的。


按位或运算符从不用于比较。它的目的是计算两个位模式的按位或。它也不是二进制加法 - 它只是一个按位或。唯一可能出现的比较是与零的隐式比较。 - D.Shawley
@D.SHawley 我说过那是什么了吗? - TLP
啊,我明白了。已经修复了。我之前有点难以表达。 - TLP

2
EXPR_A || EXPR_B

更或多或少等同于

do { my $rv = EXPR_A; if ($rv) { $rv } else { EXPR_B } }

或者用英文表述:

  1. 首先,以标量上下文评估EXPR_A。
  2. 如果值为真,则返回该值。
  3. 如果值为假,则评估并返回EXPR_B。

有时你会看到它们被链接在一起。

EXPR_A || EXPR_B || EXPR_C || EXPR_D

只是

( ( EXPR_A || EXPR_B ) || EXPR_C ) || EXPR_D

所以只需递归应用上述方法。

最终得到第一个返回 true 的表达式的结果,如果没有表达式返回 true,则得到最后一个表达式的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接