【问题标题】:To ternary or not to ternary? [closed]三元还是不三元? [关闭]
【发布时间】:2024-04-20 10:40:01
【问题描述】:

我个人是三元运算符的拥护者: () ? : ;我确实意识到它有它的位置,但我遇到过许多完全反对使用它的程序员,有些人使用它过于频繁。

你对此有何感想?你见过哪些有趣的代码使用它?

【问题讨论】:

  • 清晰时使用,混乱时避免使用。那是一个判断电话。它可以使代码更具可读性,但仅限于简单的表达式。试图总是使用它与无情地避免它一样是一种威胁。
  • 其实就是条件运算符。一个几乎重复的问题是*.com/questions/725973/…
  • 我有时使用x = x if x else y,但随后询问并在其他人的帮助下意识到它实际上只是简化为 x = x 或 y (*.com/questions/18199381/self-referencing-ternary/…)
  • 三元运算符可以用在 if..else 构造不能使用的地方,例如在返回语句中,以及作为函数参数。不使用三元组也可以达到同样的效果,但会导致代码更长,可执行文件更大。

标签: conditional-operator


【解决方案1】:

在我看来,只有在需要表达式的情况下才使用三元运算符。

在其他情况下,三元运算符似乎会降低清晰度。

【讨论】:

  • 问题在于 99% 的语言,一个表达式可以被一个函数替换......并且避免使用三元运算符的人甚至会更喜欢那个解决方案。
【解决方案2】:

好吧,它的语法很糟糕。我发现函数式 ifs 非常有用,它们通常使代码更具可读性。

我建议制作一个宏以使其更具可读性,但我相信有人会想出一个可怕的边缘情况(就像 C++ 一样)。

【讨论】:

  • 许多 BASIC 实现和变体都有一个 IF 函数来代替三元运算符。我见过许多代码库,它们在 C 中定义为宏。
  • 嗯,我在考虑函数式编程语言,但是是的。
  • “制作宏以使其更具可读性”,你真是个小丑!
【解决方案3】:

我经常在我被限制在构造函数中工作的地方使用它——例如,新的 .NET 3.5 LINQ to XML 构造——当可选参数为空时定义默认值。

人为的例子:

var e = new XElement("Something",
    param == null ? new XElement("Value", "Default")
                  : new XElement("Value", param.ToString())
);

或者(感谢asterite)

var e = new XElement("Something",
    new XElement("Value",
        param == null ? "Default"
                      : param.ToString()
    )
);

无论你是否使用三元运算符,确保你的代码可读是最重要的。任何构造都可能变得不可读。

【讨论】:

  • 或者... var e = new XElement("Something", new XElement("value", param == null ? "Default" : param.toString()));
【解决方案4】:

我对待三元运算符很像 GOTO。它们有自己的位置,但您通常应该避免使用它们以使代码更易于理解。

【讨论】:

    【解决方案5】:

    由于不能在每个子表达式上放置断点,因此调试会稍微困难一些。我很少使用它。

    【讨论】:

    • 这是我听过的反对三元运算符的最佳论据。我不赞成“不可读”的说法(在我看来,这听起来像是人们懒得习惯它),但这实际上是有实质意义的。
    • 如果你在三元组中做任何需要调试的事情,那么它可能被错误地使用了。三元组应该用于简单的分配 IMO。如果您需要单步执行三元,并且事后仅看到赋值的值还不够,那么三元并不是您的实际问题。这是操作的复杂性。在这一点上使它成为一个 if 语句。
    【解决方案6】:

    我同意 jmulder:它不应该用来代替 if,但它可以用于返回表达式或表达式内部:

    echo "Result: " + n + " meter" + (n != 1 ? "s" : "");
    return a == null ? "null" : a;
    

    前者只是一个例子,最好使用internationalisation and localisation支持复数!

    【讨论】:

      【解决方案7】:

      我尽可能使用三元运算符,除非它使代码极难阅读,但这通常只是表明我的代码可以使用一些重构。

      有些人认为三元运算符是一个“隐藏”的功能或者有些神秘,这总是让我感到困惑。这是我开始用 C 编程时学到的第一件事,我认为它根本不会降低可读性。这是语言的自然组成部分。

      【讨论】:

      • 它可能会导致可读性问题,尤其是嵌套时。
      • 我认为“非常难以阅读”有点过于宽容,但总的来说我同意你的看法。没有什么困难或神秘的。
      【解决方案8】:

      我认为应该在需要时使用三元运算符。这显然是一个非常主观的选择,但我发现一个简单的表达式(特别是作为返回表达式)比一个完整的测试要清晰得多。 C/C++ 示例:

      return (a>0)?a:0;
      

      相比:

      if(a>0) return a;
      else return 0;
      

      您也有解决方案介于三元运算符和创建函数之间的情况。例如在 Python 中:

      l = [ i if i > 0 else 0 for i in lst ]
      

      替代方案是:

      def cap(value):
          if value > 0:
              return value
          return 0
      l = [ cap(i) for i in lst ]
      

      在 Python 中(例如)可以经常看到这样的习语,这已经足够了:

      l = [ ((i>0 and [i]) or [0])[0] for i in lst ]
      

      这一行使用 Python 中逻辑运算符的属性:它们是惰性的,如果等于最终状态,则返回最后计算的值。

      【讨论】:

        【解决方案9】:

        这是一个风格问题,真的;我倾向于遵循的潜意识规则是:

        • 只计算 1 个表达式 - 所以 foo = (bar > baz) ? true : false,但不是 foo = (bar > baz && lotto && someArray.Contains(someValue)) ? true : false
        • 如果我将它用于显示逻辑,例如<%= (foo) ? "Yes" : "No" %>
        • 仅真正用于赋值;从不流逻辑(所以从不 (foo) ? FooIsTrue(foo) : FooIsALie(foo) 三元中的流逻辑本身就是一个谎言,忽略最后一点。

        我喜欢它,因为它简洁优雅,适合简单的赋值操作。

        【讨论】:

        • 在 C# 中,如果您从三元组中分配委托,然后再调用它,您可以将其用于流控制。嗯,这是一种流量控制...
        • 你的前两个例子真的很糟糕。比较的结果已经是布尔值,所以你的三元运算符没有用,只会让代码复杂化。
        • @Trillian +1 是的,应该有不同的任务。 foo = (bar > baz); 更简单
        • 对于带有一堆子句的布尔返回的情况,我喜欢使用三元条件将需求分解成更小的部分,就像使用早期返回来简化代码进行重构一样。 return bar <= baz ? false ! lotto ? false : someArray.Contains(someValue )
        【解决方案10】:

        我几乎从不使用三元运算符,因为每当我使用它时,它总是让我想得比以后尝试维护它时要多得多。

        我喜欢避免冗长,但是当它使代码更容易阅读时,我会尽量避免冗长。

        考虑:

        String name = firstName;
        
        if (middleName != null) {
            name += " " + middleName;
        }
        
        name += " " + lastName;
        

        现在,这有点冗长,但我发现它比以下更具可读性:

        String name = firstName + (middleName == null ? "" : " " + middleName)
            + " " + lastName;
        

        或者:

        String name = firstName;
        name += (middleName == null ? "" : " " + middleName);
        name += " " + lastName;
        

        它似乎只是将太多信息压缩到太小的空间中,而没有说明发生了什么。每次我看到使用三元运算符时,我总能找到一个看起来更容易阅读的替代方案......再说一次,这是一个非常主观的意见,所以如果你和你的同事发现三元运算符非常可读,那就去吧。

        【讨论】:

        • 但这并不完全相同。在第二个示例中,您将所有三个语句压缩为一行。那是降低可读性的原因,而不是三元运算符。
        • 公平地说,我已更新以纳入您的评论,但我仍然觉得它很混乱......但同样,这是主观的......我并不是说三元不可读,我是说它对我来说不可读(99% 的时间)
        【解决方案11】:

        我喜欢在调试代码中使用运算符来打印错误值,这样我就不必一直查找它们。通常我会为完成开发后不会保留的调试打印执行此操作。

        int result = do_something();
        if( result != 0 )
        {
          debug_printf("Error while doing something, code %x (%s)\n", result,
                        result == 7 ? "ERROR_YES" :
                        result == 8 ? "ERROR_NO" :
                        result == 9 ? "ERROR_FILE_NOT_FOUND" :
                        "Unknown");
        }
        

        【讨论】:

        【解决方案12】:

        我是它的忠实粉丝......在适当的时候。

        这样的东西很棒,而且,就我个人而言,我不觉得阅读/理解太难:

        $y = ($x == "a" ? "apple"
           : ($x == "b" ? "banana"
           : ($x == "c" ? "carrot"
           : "default")));
        

        我知道这可能会让很多人感到畏缩。

        在 PHP 中使用它时要记住的一点是它如何与返回引用的函数一起工作。

        class Foo {
            var $bar;
        
            function Foo() {
                $this->bar = "original value";
            }
        
            function &tern() {
                return true ? $this->bar : false;
            }
        
            function &notTern() {
                if (true) return $this->bar;
                else      return false;
            }
        }
        
        $f = new Foo();
        $b =& $f->notTern();
        $b = "changed";
        echo $f->bar;  // "changed"
        
        $f2 = new Foo();
        $b2 =& $f->tern();
        $b2 = "changed";
        echo $f2->bar;  // "original value"
        

        【讨论】:

        • 这是因为三元运算符计算结果并按其值返回 $this -> bar 而另一个只是简单地返回变量。
        • 奇怪的是这不会导致错误,因为您因此没有返回参考。
        【解决方案13】:

        仅用于简单的表达式

        int a = (b > 10) ? c : d;
        

        不要链接或嵌套三元运算符,因为它难以阅读和混淆:

        int a = b > 10 ? c < 20 ? 50 : 80 : e == 2 ? 4 : 8;
        

        此外,在使用三元运算符时,请考虑以提高可读性的方式格式化代码:

        int a = (b > 10) ? some_value                 
                         : another_value;
        

        【讨论】:

        • 完全同意前几条陈述,但完全不同意您关于“提高可读性”的示例。如果您要使用多行代码,为什么不使用 if 语句?
        • 只是因为 if else 对于简单的决策来说更加冗长:int a = 0; if(b > 10) a = some_value;否则 a = another_value;你喜欢什么?
        • @d03boy:因为 if-statement 只是一个语句,当你想要的只是一个表达式时,它是行不通的。
        • @roe 在某些语言中的 if 是表达式(例如在 Scala 中),所以 val x = if(true) 0 else 1 是完全合法的
        • @om-nom-nom 就是一个例子,这将使它成为一个 if 表达式,而不是一个 if 语句,并且本质上与 ?:-operator 相同。
        【解决方案14】:

        我最近看到了三元运算符的变体(嗯,有点),它使标准的“()?:”变体似乎是清晰的典范:

        var Result = [CaseIfFalse, CaseIfTrue][(boolean expression)]
        

        或者,举一个更具体的例子:

        var Name = ['Jane', 'John'][Gender == 'm'];
        

        请注意,这是 JavaScript,所以其他语言可能无法实现类似的功能(谢天谢地)。

        【讨论】:

        • 哇,太糟糕了!想象一下将几个嵌套在一起!我能看到的唯一有用的东西是,如果你有一个返回 2 元素数组的函数: var Name = getNames()[Gender == 'm']; ...但这更不可读!
        【解决方案15】:

        仅当:

        $var = (simple > test ? simple_result_1 : simple_result_2);
        

        KISS.

        【讨论】:

          【解决方案16】:

          如果没有三元运算符,谁会赢得混淆代码竞赛?!

          我个人喜欢在适当的时候使用它,但我认为我永远不会嵌套它。它非常有用,但它有一些缺点,因为它使代码更难阅读,并且在其他一些语言的其他操作中使用(例如 Groovy 的 null-check)。

          【讨论】:

            【解决方案17】:

            三元 ?: 操作符仅仅是过程式if 构造的功能等价物。因此,只要您不使用嵌套的 ?: 表达式,支持/反对任何操作的功能表示的参数都适用于此处。但是嵌套三元运算可能会导致代码完全混乱(读者练习:尝试编写一个解析器来处理嵌套三元条件,您将体会到它们的复杂性)。

            但在很多情况下,保守地使用 ?: 运算符会导致代码实际上更容易阅读。例如:

            int compareTo(Object object) {
                if((isLessThan(object) && reverseOrder) || (isGreaterThan(object) && !reverseOrder)) {
                   return 1;
                if((isLessThan(object) && !reverseOrder) || (isGreaterThan(object) && reverseOrder)) {
                   return -1;
                else
                  return 0;
            }
            

            现在比较一下:

            int compareTo(Object object) {
                if(isLessThan(object))
                    return reverseOrder ? 1 : -1;
                else(isGreaterThan(object))
                    return reverseOrder ? -1 : 1;
                else
                   return 0;
            }
            

            由于代码更紧凑,语法噪音更少,并且通过明智地使用三元运算符(仅与 reverseOrder 属性有关)最终结果并不是特别简洁。

            【讨论】:

            • 我仍然主张在每个不是三元的 if/then/else 结构上使用 accolades,所以你的第二个例子缺少一些恕我直言。
            • 是的,它很实用。它就像一个小函数,只有一个布尔参数,并返回你想要的 any 类型!实际上是一个整洁的运算符。
            【解决方案18】:

            对于简单的 if 情况,我喜欢使用它。实际上,读取/编码例如作为函数或类似事物的参数要容易得多。也为了避免新行,我喜欢保留我所有的 if/else。

            在我的书中嵌套它将是一个很大的no-no

            所以,继续,对于单个 if/else,我将使用三元运算符。对于其他情况,常规 if/else if/else(或 switch)。

            【讨论】:

              【解决方案19】:

              有趣的轶事:我已经看到优化器对三元运算符的权重比等效的 if 更不“重”。我在 Microsoft 编译器中注意到了这一点,但它可能会更普遍。

              特别是像这样的函数会内联:

              int getSomething()
              {
                  return m_t ? m_t->v : 0;
              }
              

              但这不会:

              int getSomething()
              {
                  if( m_t )
                      return m_t->v;
                  return 0;
              }
              

              【讨论】:

                【解决方案20】:

                我通常在这样的事情中使用它:

                before:
                
                if(isheader)
                    drawtext(x, y, WHITE, string);
                else
                    drawtext(x, y, BLUE, string);
                
                after:
                
                    drawtext(x, y, isheader == true ? WHITE : BLUE, string);
                

                【讨论】:

                • 当然,在大多数语言中,您也不需要该三元组的“==true”部分。
                • 我意识到,尽管我倾向于将其放入只是为了使代码更具可读性,因为编译器应该将其优化为与没有 ==true 时相同的东西
                • no 语言你可能需要 "==true"
                • 我很难决定是否投赞成票。这个例子很好,但 ==TRUE 是我无法忍受在其他人的代码中看到的东西。
                【解决方案21】:

                我非常喜欢。当我使用它时,我把它写成 if-then-else:条件、真动作和假动作各一行。这样,我可以轻松地嵌套它们。

                例子:

                x = (a == b ? (sqrt(a) - 2) : (a*a + b*b) ); x = (a == b ? (sqrt(a) - 2) : (a*a + b*b) ); x = (a == b ? (c > d ? (sqrt(a) - 2) : (c + cos(d)) ) : (a*a + b*b) );

                对我来说,这很容易阅读。它还可以轻松添加子案例或更改现有案例。

                【讨论】:

                • 在我看到这个例子之前,我以为我是一个超级粉丝。这需要一些时间来适应。我将它们用于单行,而不是块。
                • 去给自己买个Lisp吧,你这个封闭的同性恋。
                • 这看起来很可怕。而且我认为自己是运营商的粉丝。
                【解决方案22】:

                链式我很好 - 嵌套,不是那么多。

                我倾向于在 C 中更多地使用它们,因为它们是一个具有价值的 if 语句,因此它减少了不必要的重复或变量:

                x = (y < 100) ? "dog" :
                    (y < 150) ? "cat" :
                    (y < 300) ? "bar" : "baz";
                

                而不是

                     if (y < 100) { x = "dog"; }
                else if (y < 150) { x = "cat"; }
                else if (y < 300) { x = "bar"; }
                else              { x = "baz"; }
                

                在这样的作业中,我发现重构更少,更清晰。

                另一方面,当我使用 ruby​​ 工作时,我更有可能使用 if...else...end,因为它也是一个表达式。

                x =   if (y < 100) then "dog"
                    elif (y < 150) then "cat"
                    elif (y < 300) then "bar"
                    else                "baz"
                    end
                

                (虽然,诚然,对于这么简单的事情,我可能只是使用三元运算符。)

                【讨论】:

                  【解决方案23】:

                  我使用并推荐三元组以避免在逻辑微不足道的情况下出现代码行。

                  int i;
                  if( piVal ) {
                      i = *piVal;
                  } else {
                      i = *piDefVal;
                  }
                  

                  在上述情况下,我会选择三元,因为它的噪音更小:

                  int i = ( piVal ) ? *piVal : *piDefVal;
                  

                  同样的条件返回值是很好的候选:

                  return ( piVal ) ? *piVal : *piDefVal;
                  

                  我认为紧凑性可以提高可读性,进而有助于提高代码质量。

                  可读性始终取决于代码的受众。

                  读者必须能够不费任何脑力地理解a ? b : c 模式。 如果你不能假设这一点,请选择长版。

                  【讨论】:

                  • 我同意。一旦你了解了它们是如何工作的,它就会更具可读性。三元运算符是自 foreach 循环以来最棒的东西!
                  【解决方案24】:

                  我喜欢 Groovy 的三元运算符的特殊情况,称为 Elvis 运算符:?:

                  expr ?: default
                  

                  如果不为空,则此代码的计算结果为 expr,如果为空,则为默认值。从技术上讲,它并不是真正的三元运算符,但它肯定与它相关,并且可以节省大量时间/打字。

                  【讨论】:

                  【解决方案25】:

                  如果您的三元运算符最终占据整个屏幕宽度,那么我不会使用它。我只检查一个简单的条件并返回单个值:

                  int x = something == somethingElse ? 0 : -1;
                  

                  我们实际上在生产中有一些像这样令人讨厌的代码......不好:

                  int x = something == (someValue == someOtherVal ? string.Empty : "Blah blah") ? (a == b ? 1 : 2 ): (c == d ? 3 : 4);
                  

                  【讨论】:

                  • 实际上有一种语言写 string.Empty 有意义吗? “”怎么了?
                  【解决方案26】:

                  三元运算符对于简洁地生成逗号分隔列表非常有用。这是一个 Java 示例:

                      int[] iArr = {1, 2, 3};
                      StringBuilder sb = new StringBuilder();
                      for (int i = 0; i < iArr.length; i++) {
                          sb.append(i == 0 ? iArr[i] : ", " + iArr[i]);
                      }
                      System.out.println(sb.toString());
                  

                  它产生:“1, 2, 3”

                  否则,最后一个逗号的特殊大小写会很烦人。

                  【讨论】:

                    【解决方案27】:

                    如果您正在尝试减少代码中的行数或正在重构代码,那就去做吧。

                    如果您关心下一个程序员必须多花 0.1 毫秒才能理解表达式,那么无论如何都去吧。

                    【讨论】:

                      【解决方案28】:

                      我喜欢它们,尤其是在类型安全的语言中。

                      我不明白这是怎么回事:

                      int count = (condition) ? 1 : 0;
                      

                      比这更难:

                      int count;
                      
                      if (condition)
                      {
                        count = 1;
                      }
                      else
                      {
                        count = 0;
                      }
                      

                      我认为三元运算符使一切都比其他运算符更简单、更简洁。

                      【讨论】:

                      • 当变量为常量时,三元初始化在 D 或 C++ 中更加有用。例如const int count = ...;
                      • 嗯,你有点歪曲if/else 那里不需要的大括号。
                      • 另外,如果conditionbool you could just do int count = condition;
                      • @bobobobo 这个if/else 带大括号是大多数程序员重写三进制的方式..
                      • @bobobobo if/else w/o 大括号只是自找麻烦。某人添加一行太容易了,但忘记了它需要大括号来执行他们期望的操作(将附加行作为块的一部分执行):*.com/a/381274/377225
                      【解决方案29】:

                      对于简单的任务,例如根据条件分配不同的值,它们非常棒。不过,当有更长的表达式取决于条件时,我不会使用它们。

                      【讨论】:

                        【解决方案30】:

                        如果您和您的同事了解他们的工作并且他们不是在大量群体中创建的,我认为他们可以使代码不那么复杂并且更易于阅读,因为代码更少。

                        我认为三元运算符使代码更难理解的唯一情况是一行中包含三个以上或门厅的情况。大多数人不记得它们是基于正确的优先级,当你有一堆它们时,阅读代码就会变成一场噩梦。

                        【讨论】: