短路评估顺序和前缀增量运算符

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 确保qr 属于&&。这意味着&& 具有更高的优先级。我应该注意,当我说评估必须从左到右进行时,这仅在涉及副作用时才成立(即您的++)。否则,您可以根据自己的喜好进行评估。关键是操作的包围是正确的。另一方面,短路语义必须从左到右工作,否则事情会变得非常奇怪,非常快。 @MattJenkins 例如,考虑短路的常见用法; p != null && p.x == 1,这确保我们无法通过评估 p.x 获得空指针,如果 pnull,则无法到达表达式的第二部分。在评估的非短路或无序语义中,p.x 可以在空检查之前执行,甚至可以在旁边执行,显然我们不希望这样。现在考虑p == null || p.x != 0 && p.x != 1。 && 绑定得更紧了,但是如果先执行那就是灾难了! @MattJenkins 虽然,为了补充最后一个例子,如果我们没有优先权怎么办?那么表达式将解析为(p == null || p.x != 0) && p.x != 1。如果我们执行这个表达式,那么短路会防止 || 中的 null 取消引用,但是 null 值可能会到达 p.x != 1 检查...并繁荣。

以上是关于短路评估顺序和前缀增量运算符的主要内容,如果未能解决你的问题,请参考以下文章

短路逻辑评估运算符

我将如何修改运算符“&&”和“||”的定义让他们不使用短路评估?

前缀/后缀增量运算符

前缀/后缀增量运算符

if-then-else 运算符的短路评估(例如 ?: 在 C 中)

当运算符优先级说不应该时,为啥短路评估会起作用?