短路评估顺序和前缀增量运算符
Posted
技术标签:
【中文标题】短路评估顺序和前缀增量运算符【英文标题】:Order of short circuit evaluation and prefix increment operators 【发布时间】:2018-09-13 09:22:04 【问题描述】:在 java 中评估布尔表达式时,我发现自己对短路评估和前缀增量运算符感到有些困惑。考虑以下几点:
int e=20;
int f=25;
if(++e>21 || ++e>21 && f>30)
System.out.println("Hi");
System.out.println(e);
我知道如果 ++e 大于 21,IF 语句的其余部分将跳过(由于短路评估)。但在这种情况下,它没有,因为第一部分不正确,所以我们继续使用 AND 语句。此时,e还是20吗?或者在短路评估期间,它是否上升到 21?
好吧,我假设此时,我们评估 AND 语句(就像我们通常在 OR 之前所做的那样),我们将 1 加到 e,我假设现在变成 21?它是假的,因此整个 AND 语句都是假的。
此时我们是否返回并执行 OR 语句?因为它在AND之后? e现在不应该被抽到22吗?并且由于它是一个 OR 语句,它应该是 TRUE OR FALSE,它应该是 TRUE,并且屏幕上应该出现“Hi”。但事实并非如此。
奇怪的是,当代码完成时,e 的值是 22。 22 是我们需要使 IF 语句为真的值,但里面的条件没有运行。
我非常困惑。
【问题讨论】:
你不应该尽可能地压缩你的代码。您的目标是使代码清晰易懂,而不是尽可能减少行数。这种类型的编码是不良编程习惯的一个例子。 但要回答您的问题,前增量/前减量运算符在表达式执行之前进行操作,后增量/后减量在表达式执行后进行操作。 @madcobra 我同意,这似乎是一个简单的教育练习,而不是真正的代码。至少,我希望不是!但我支持这一点;请不要在真实代码的表达式中编写带有副作用的代码! 【参考方案1】:让我们解开短路究竟是什么!
int e=20;
int f=25;
if((++e>21) || (++e>21 && f>30))
System.out.println("Hi");
System.out.println(e);
所以,如果or
条件的左侧计算结果为真,我们不需要再做任何事情,所以让我们更明确一点:
int e=20;
int f=25;
if (++e > 21)
System.out.println("Hi");
// the or becomes an else if, if the first condition fails then we check the second
else if (++e>21 && f>30)
System.out.println("Hi");
System.out.println(e);
现在,让我们更明确地说明第一次递增发生的时间:
int e=20;
int f=25;
e += 1;
if (e > 21) // e = 21, 21 is not > 21
System.out.println("Hi");
// We need to test this condition, as you've said
// e is still 21
else if (++e>21 && f>30)
System.out.println("Hi");
System.out.println(e);
再一次,让我们解开and
在短路方面的含义
int e=20;
int f=25;
e += 1;
if (e > 21) // e = 21, 21 is not > 21
System.out.println("Hi");
// We need to test this condition, as you've said
// e is still 21
else if (++e > 21) // the and becomes a nested if, first the left must be true before we test the right
if (f > 30)
System.out.println("Hi");
System.out.println(e);
再一次,让我们明确一下这个增量!
int e=20;
int f=25;
e += 1;
if (e > 21) // e = 21, 21 is not > 21
System.out.println("Hi");
// We need to test this condition, as you've said
// e is still 21
else
e += 1;
if (e > 21) // e is now 22, this condition succeeds
if (f > 30) // f is not >30, this condition fails!
System.out.println("Hi");
// e is still 22
// e is still 22
System.out.println(e);
希望这能说明一切!
编辑:实际上,在重新阅读了几次您的问题之后,您似乎对逻辑运算符的优先级感到困惑。您似乎认为and
应该在or
之前执行,对吧?大概是因为and
具有更高的优先级。然而,这意味着隐式括号首先像这样围绕and
;
p || q && r == p || (q && r)
您应该可以看到,在这种形式中,必须先评估外部or
,然后再评估and
。我们总是从左到右评估!
【讨论】:
如果我们从左到右求值,那么为什么运算符有优先级?整个优先点不就是允许一次先行吗? @mattjenkins 不,优先点是确保操作的分组。例如,如果 * 和 + 具有相同的优先级,那么3 + 8 * 4
将自然地括起来为 (3+8)*4
,因为它们是左关联运算符。这显然是不正确的。通过使 * 具有更高的优先级,我们强制包围在 8*4
周围发生。这一切都与解析表达式有关,而不是评估它们。
@MattJenkins 同样适用于布尔表达式,我们希望p || q && r
确保q
和r
属于&&
。这意味着&&
具有更高的优先级。我应该注意,当我说评估必须从左到右进行时,这仅在涉及副作用时才成立(即您的++
)。否则,您可以根据自己的喜好进行评估。关键是操作的包围是正确的。另一方面,短路语义必须从左到右工作,否则事情会变得非常奇怪,非常快。
@MattJenkins 例如,考虑短路的常见用法; p != null && p.x == 1
,这确保我们无法通过评估 p.x
获得空指针,如果 p
是 null
,则无法到达表达式的第二部分。在评估的非短路或无序语义中,p.x
可以在空检查之前执行,甚至可以在旁边执行,显然我们不希望这样。现在考虑p == null || p.x != 0 && p.x != 1
。 && 绑定得更紧了,但是如果先执行那就是灾难了!
@MattJenkins 虽然,为了补充最后一个例子,如果我们没有优先权怎么办?那么表达式将解析为(p == null || p.x != 0) && p.x != 1
。如果我们执行这个表达式,那么短路会防止 || 中的 null 取消引用,但是 null 值可能会到达 p.x != 1
检查...并繁荣。以上是关于短路评估顺序和前缀增量运算符的主要内容,如果未能解决你的问题,请参考以下文章
我将如何修改运算符“&&”和“||”的定义让他们不使用短路评估?