【问题标题】:&&= and ||= operators [duplicate]&&= 和 ||= 运算符 [重复]
【发布时间】:2011-04-27 03:41:02
【问题描述】:

可能的重复:
Why doesn't Java have compound assignment versions of the conditional-and and conditional-or operators? (&&=, ||=)
Why does a “&&=” Operator not exist?

今天上班写了如下LOC(b和b1的真实身份保密:)

b &&= b1; // meaning b = b && b1; 

我盯着它看了几秒钟,意识到不存在这样的运算符。可以肯定的是,我点击了编译,但它失败了。为了确定我参考了标准。

是否有没有这样的运营商的具体原因?我能想到一些:

  1. 由于 && 的短路评估,b &&= b1b = b && b1 可能不等价。
  2. &&= 很丑
  3. &&= 很少需要

我并不是说拥有这样的运算符会很有用,不。我也没有声称上述三个原因中的任何一个或全部都不足以避免创建该运算符。我的问题如下:我正在监督是否有更严重的原因?

【问题讨论】:

  • b &= isSomethingWithSideEffects(); 无论如何都会运行该函数。但是如果将其更改为&&= 大概不会在 b 为假的情况下,不是吗?
  • 抱歉,我不明白为什么 Dingo 的评论被点赞了。有人可以解释一下吗?
  • @John - 这是一个现已删除的建议的反例,即&= 等同于&&=(正如我的评论)
  • 忘记这个问题 - 我很想知道bb1 的真实身份的秘密会给我带来什么不为人知的力量!
  • @Close-Voters:请考虑不要关闭这个问题,因为潜在的重复项都涉及 Java,答案是 Java 中没有 &&= 因为 C 中没有。没有线程提供了一个令人满意的答案,我希望在这里得到。只是考虑一下。谢谢。

标签: c# c++ c logical-operators


【解决方案1】:

我不知道为什么问题和一些答案都提到相应逻辑运算符的短路行为是一个潜在问题。

定义&&=||= 运算符绝对没有与短路相关的问题。它们应该用+= 和其他类似的运算符统一定义,这意味着a &&= b 应该等同于a = a && b,但a&&= 版本中只被评估一次。这反过来意味着如果 a 最初为零,则根本不会评估 b。很简单。

所以,它们在语言中不存在的唯一原因是,嗯,“只是因为”。

【讨论】:

  • 没错。 Perl 有这些操作符,具有这种行为;虽然我听到了很多对 Perl 的批评,但我从未听到有人抱怨 &&= 令人困惑。
  • 关于短路的事情很好,但从技术上讲,如果 a 有副作用,a = a + b 不等于 a += b,例如是一个打印一些东西并返回一个引用的函数(在第一种情况下将打印两次,在第二种情况下只打印一次)。
  • @Armen Tsirunyan:嗯,我并不想让它正式成为级长。语言标准将a += b 定义为等同于a = a + b,只是在+= 版本中a 只计算一次。同样的方式可以定义&&=
  • @T.E.D.:是的,我真的很想要这样的接线员。 &&|| 运算符的行为已经与其他二元运算符(例如 +)有很大不同,我们一直很好地适应这种情况。我认为&&=||= 与其他形式的分配相比也有明显不同的行为,即使它涉及对 RHS 的可选评估。
  • @T.E.D.:a && b; b 也没有被评估。
【解决方案2】:

我以前也想要它们。我怀疑丑陋本身就是决定因素,短路行为是我迄今为止听到的反对它们的最佳论据。

另一个促成因素是 C 被设计为接近金属操作,几乎所有操作符都直接对应于主要架构中的指令。我认为x86、PPC等上没有直接实现b &&= b1;的指令

【讨论】:

  • +1。 PDP-11 上可用的操作可能更多是一个决定因素,但我仍然认为你在那里找到了一些东西。
  • @T.E.D.:当然,但是现代主要架构从过去的架构中继承了大部分指令。尤其是在公共区域。
  • 嗯?短路是他们的论据。没有它,只需将 &= 与布尔值一起使用。至于说明,使用与b && ( b = b1 ); 相同的说明(但当然只有一次评估b)。编译器在分支和按位与之间进行选择。
  • @Potatoswatter:反对的论点是它们将是唯一短路的赋值运算符。而且你的小 sn-p 没有所需的行为,它需要是if (b) b = (b1 != 0);。您的版本与运算符 && 不同,可能会产生 0 或 1 以外的值。
  • @Potatoswatter:大多数(全部?)其他复合赋值运算符实际上使用 较少 指令,而不是将运算符结果保存到临时位置所需的指令。例如c = a + b; 变为 (MOV C, A; ADD C, B) 和 a += b; 变为 (ADD A, B)。 &&= 的逻辑要复杂得多。
【解决方案3】:

运营商不存在的最大原因可能是 K&R 没有想到任何吸引人的方式来定义它们。我有时也想要一个 ->= 运算符(ptr->=next 相当于 ptr = ptr->whatever)。

我认为 &&= 的一个问题是,以下哪个最有用或应该是哪个并不明显:

如果 (lhs && rhs) lhs = 1;否则 lhs = 0; 如果 (!rhs) lhs = 0;否则 lhs = !(!lhs)); 如果 (lhs && !rhs) lhs = 0; 如果 (!rhs) lhs = 0; 第一个变体是语法中最清楚地暗示的变体,但从实际的角度来看,如果两个项都不为零,则将左侧单独保留通常比将其设置为“1”更有用。

顺便说一句,我经常希望逗号运算符的变体可以评估左侧,保存值,然后评估右侧,并返回左侧的值。相当于:

int foo(int p1, int p2) 返回 p1; 除了适用于任何类型(p2 不必与 p1 类型相同,并且可以为 void),并且具有保证的从左到右的评估顺序。对于诸如使用非单位步骤的后增量索引之类的事情会非常方便,例如 arr[ptr ~, ptr+=2];或者对于某些类型的数据交换操作,例如,var1 = (var2 ~, var2=var1);等等。

【讨论】:

  • 在 C 中,所有复合赋值运算符 (op=) 都定义为 lhs = lhs op rhs,而 lhs 表达式只计算一次。因此,我认为&&= 的定义方式几乎没有歧义(以相同的方式)。
  • @Michael Burr:从语法上讲,这将是自然的实现,但在实践中,有时第三个会比第一个更有用,但我想不出第一个实际上会那么多更有用。
  • 我提出了这个等同于现在已删除的问题:if (lhs) lhs = (rhs != 0); 似乎有预期的短路行为。但是,执行if (lhs) lhs = rhs; 可能会更快,它也会短路但不会将所有非零 rhs 强制为 1。
【解决方案4】:

因为a && b 的结果总是01,所以我认为这个解释对于C99 的_Bool 类型只会是明确的。由于在创建 C 时它不存在,因此不包括该运算符。现在没有人能轻易地在 C 中添加另一个运算符,因为这会对所有现有的解析器产生影响。

【讨论】:

    【解决方案5】:

    就我个人而言,我会投票支持您选择的第一个理由。布尔运算符具有短路语义,如果将其转换为赋值运算符,这将导致一些非常棘手的情况。要么您不再使它们短路,要么您创建了一些奇怪的“可选”赋值运算符(仅在 LHS 上的值已经非零时才执行右侧的操作并在结果中赋值)。无论哪种方式,您都会产生微妙的错误,因为人们会期待其他行为。

    【讨论】:

      【解决方案6】:

      编辑:语言错误,但仍然适用

      我同意你的三个理由,尽管在编写自定义反序列化例程时,我曾对缺少此运算符感到遗憾。在一些不正确序列化的对象并不是真正“异常”的情况下(并且为了防止 C# 中非常常见的故障导致异常开销),我会使用布尔返回值来表示反序列化操作是否成功。

      这段代码完全是理论上的,Nested1、Nested2和Nested3都是结构体:

      public struct ComplexStruct
      {
          private Nested1 nested1;
          private Nested2 nested2;
          private Nested3[] nested3;
      
          public bool Read(Reader reader)
          {
             bool ret = true;
             int nested3Length = 0;
      
             ret &&= nested1.Read(reader);
             ret &&= nested2.Read(reader);
             ret &&= reader.ReadInt32(ref nested3Length);
      
             for(int i = 0; (ret && i < nested3Length); i++)
             {
                ret &&= nested3[i].Read(reader);
             }
      
             return ret;
          }
      }
      

      【讨论】:

      • 嗯,当我需要它时,这几乎是相同的场景(循环部分)。我现在将编辑我的问题以澄清这一点
      • 在这种情况下,你可以早点return false;
      • 酷...在我的情况下,我想要短路行为,所以我最终在大多数情况下使用 ret = ret && xxx...这有点冗长,但至少这清楚地表明我正在做短路布尔比较
      猜你喜欢
      • 2020-01-25
      • 1970-01-01
      • 1970-01-01
      • 2015-09-18
      • 1970-01-01
      • 2015-02-23
      • 2016-01-09
      • 2021-04-14
      • 2016-02-03
      相关资源
      最近更新 更多