【问题标题】:Reason for the existence of non-short-circuit logical operators非短路逻辑运算符存在的原因
【发布时间】:2012-03-05 02:43:03
【问题描述】:

当与 boolean 操作数一起使用时,& 和 | 根据 Section 15.22.2 of the JLS 成为 逻辑 运算符。然而,与&& 和|| 不同的是,它们不会短路。他们总是评估双方。我有一个愚蠢的问题:为什么效率较低的非短路逻辑运算符(&,|)仍然存在,而我们拥有更高效的短路逻辑运算符(&&,@987654331 @)?我的意思是,与短路逻辑运算符相比,非短路逻辑运算符的真正用途是什么?换句话说,总是使用非短路逻辑运算符来评估双方有什么用?

【问题讨论】:

  • 这与效率无关,它们有不同的语义
  • 这就是 C 所做的。 / 实际上在某些情况下它们可以更快,因为它们可能会减少分支。评估 rhs(邪恶)也可能有副作用。
  • @TomHawtin-tackline:C 不会短路 & 和 |。与 Java 一样,它会短路 && 和 ||,但不会短路 & 和 |。
  • 感谢 T.J. 的编辑。克劳德

标签: java logical-operators


【解决方案1】:

更新答案:

抱歉,我在你的问题中错过了“逻辑”这个词,即使它在那里。 (我冒昧地通过编辑强调了一点。)

考虑您希望任何副作用总是发生的情况,无论左侧表达式的计算结果是true 还是false。例如,对比:

if (foo() & bar()) {
    // Only call this if both operations returned true
}

与

if (foo() && bar()) {
    // Only call this if both operations returned true
}

假设foo 和bar 都有我们希望发生的效果,无论foo 返回true 还是false。在上面的第一个中,我知道 bar 总是会被调用并产生效果。当然,在后者中,bar 可能会也可能不会被调用。如果我们没有非短路版本,我们将不得不使用临时变量:

boolean fooResult, barResult;
fooResult = foo();
barResult = bar();
if (fooResult && barResult) {
    // ...
}

你可能会争辩(我可能会)你应该这样做无论如何,因为它太容易误读if (foo() & bar()),但我们开始了,一个非短的务实理由电路版本。

原答案:

您如何建议&(或|)成为短路运算符?使用&& 和||,这是有道理的,因为您正在处理布尔条件:它们可以是真或假,没有灰色阴影。但是& 和| 处理的是位,而不是布尔值。结果是一个数字。我的意思是,如果左侧是0,我猜& 无法评估右侧,同样| 无法评估左侧是否全位开启类型是,但我认为让每个运算符的一个边缘情况显着(与 254 个或更多其他情况相比)没有多大意义。

【讨论】:

  • 在 Java 中 & 和 |如果应用于布尔类型,则不适用于位。
  • 对不起,如果可以的话,我会删除反对票。我只是因为你错了才尝试过,你不同意吗? :>
  • @SuzanCIOC:谢谢,我已经略过了问题中的“逻辑”一词!更新了答案并编辑了问题,以帮助其他人避免同样的错误。
  • 事实上,不短路还有效率方面的原因:分支可能很昂贵,如果两个条件都很便宜,使用@987654343 实际上可以更快 @ 和 |。我不会推荐它,除非 a) 你真的非常热衷于性能,b) 你可以生成基准数据来支持你。
  • 依赖non-short-circuit 逻辑(尤其不是无处不在,但在某些地方)对我来说似乎是代码设计中的一个缺陷。它让你牢记这样一个事实,即所有的表达元素都会产生效果。
【解决方案2】:

在某些情况下,布尔表达式的组件涉及您希望在所有情况下都执行的操作。考虑以下检查密码有效性的示例:

while ( !password.isValid() & (attempts++ < MAX_ATTEMPTS) ) {

    // re-prompt

}

如果由于短路而未评估第二个条件,attempts 将永远不会增加。从而提高了程序员的灵活性。

【讨论】:

  • 但是如果改变顺序也可以用&amp;&amp;写:)
【解决方案3】:

我的案例(C++):

void setFields(Parameters bundle)
{
  if (setIfDifferent(&field1, bundle.value1) | 
      setIfDifferent(&field2, bundle.value2) |
      setIfDifferent(&field3, bundle.value3)) {
    storeState();
  }
}

setIfDifferent() 如果对象的字段不同,则将其设置为新值,在这种情况下,它返回 true;或者如果字段的值和新值相同,则返回 false。所以,我们想尝试设置所有字段,如果其中任何一个发生了变化,那么我们想存储新对象的状态。

【讨论】:

    【解决方案4】:

    您可以在逻辑表达式中产生一些副作用,例如您可以在检查的同时进行赋值。如果只评估一个部分,这可能会出错。

    现在记不起好例子了,但记得我有时需要“非短路”运算符。

    嗯....下面是错误的示例,如果没有“非短路”OR,它将无法工作:

    if( (object1=getInstance1()).getNumber() == 1 || (object2=getInstance2()).getNumber() == 2 ) {
    
        // do something which requires bot object1 and object2 assigned
    
    }
    

    【讨论】:

    • +1 指出“逻辑表达式中的一些副作用”。
    【解决方案5】:

    就我而言,我有两种方法可以比较两个不同但相关的对象(Object2 是 Object1 的一个属性),以查看是否有任何变化。如果其中任何一个被更新,则需要进行更新,但两者都需要进行评估,以便如果两者都已更改,则对象将被修改。因此,需要进行单管道“OR”比较。

    前:

    if (compare(object1, currentObject1) | comparison(object2, currentObject2)) {
        updateObject1(object1);
    }
    

    【讨论】:

      【解决方案6】:

      技术上,& 和 |不是逻辑的,它们是位运算符,当与布尔值相关联时成为逻辑运算符。

      有时您希望在逻辑表达式中包含赋值表达式。

      说:

      if(a = (checkForSomeCondition()) | b = checkForAnotherCondition())
      {
       //now do something here with a and b that has been cached
      }
      

      如果我使用了 ||,我将无法执行上述检查,并且必须将分配拆分为单独的语句。我在应用开发过程中从未遇到过这样的场景,但在编写算法时遇到过几次。

      当然,您可以在逻辑表达式上使用一元运算符或通过引用将变量传递给谓词,但这些情况似乎不如上述情况常见。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-02-04
        • 2013-06-02
        • 1970-01-01
        • 2015-07-14
        • 2019-05-07
        相关资源
        最近更新 更多