是否应该使用三元运算符?

205

我个人是三元运算符的支持者:() ? :

我知道它有其适用场合,但我遇到过许多程序员完全反对使用它,还有一些程序员过于频繁地使用它。

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


9
当表达清晰明了时使用它,当它让人感到困惑时则避免使用。这是一个判断的问题。它可以使代码更易读,但仅适用于简单表达式。试图总是使用它与过度避免它一样有害。 - Abel
4
实际上,这是条件操作符。一个类似的问题在http://stackoverflow.com/questions/725973/what-do-fellow-net-developers-think-about-the-conditional-operator/726005上。 - Daniel Daranas
我有时会使用 x = x if x else y,但后来询问了别人的意见,得知它实际上可以简化为 x = x or y(http://stackoverflow.com/questions/18199381/self-referencing-ternary/18199562#18199562)。 - Scruffy
三元运算符可用于 if..else 结构无法使用的地方,例如在返回语句和函数参数中。虽然可以不使用三元运算符来实现相同的功能,但会导致代码变得更长,可执行文件变得更大。 - Arif Burhan
54个回答

2

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

expr ?: default

如果expr不为空,则该代码计算为expr,否则计算为default。严格来讲,这并不是一个三目运算符,但它与之相关,并且可以节省很多时间和打字。


是的,我也喜欢那个 - 它在C#中是??,空合并运算符:https://dev59.com/D3VC5IYBdhLWcg3wfhKL - Jarrod Dixon

1

我认为逻辑表达式中的条件数量增加会使其更难阅读。这对于if语句和三元运算符都是正确的。在完美的世界中,应该有一个可总结的原因来选择某个分支而不是其他分支。如果您的解释是“只有当发生此状态集群时”,那么很可能真的更像是“业务规则”。

然而,在现实世界中,我们不会添加中间步骤来将状态折叠成一个可表达的状态,仅仅是为了遵守理想情况。我们已经推断出多个状态,并且必须决定如何处理它们。

喜欢三元运算符,因为使用if语句可以做任何事情

if( object.testSomeCondition()) {
    System.exec( "format c:" );
}
else {
    a++;
}

另一方面:
a += ( object.testSomeCondition() ? 0 : 1 );

这段代码明确表明目标是找到a的值。当然,为了达成这个目标,可能不应该有过多的副作用。

  • 在我决定是否有时间重构上游条件以回答更简单的问题之后,我会使用if来处理长或复杂的条件。但是当我使用if时,我仍然尝试进行并行处理,只是在不同的条件下。

      if (  user.hasRepeatedlyPressedOKWithoutAnswer()
         && me.gettingTowardMyLunchtime( time )
         ) {
          ...
      }
    
  • 此外,我的目标是接近单流程处理。因此,我经常尝试使用else,而if只是通用路径上的一步。当您进行大量单流程处理时,很难让错误隐藏在代码中等待那个将跳出并破坏事物的条件。

  • 正如我上面所说,如果您使用三元运算符设置一个值,或者您想要测试少量情况以设置它为一个值,则我只是喜欢三元运算符的可读性

  • 有一个例外->没有复杂的true子句

      a = b == c ? ( c == d ? ( c == e ? f : g ) : h ) : i;
    

当然可以分解为:

a = b != c ? i
  : c != d ? h
  : c == e ? f
  :          g
  ;

看起来像是一个(压缩的)真值表。

记住,可读性有更重要的因素。其中之一是块长度,另一个是缩进级别。在三元运算中做简单的事情不会导致进一步的缩进级别。


1
三元运算符一流。如果你正确地格式化它们,它们并不复杂。以闰年的例子为例来自paxdiablo:
$isLeapYear =
   (($year % 400) == 0)
   ? 1
   : ((($year % 100) == 0)
      ? 0
      : ((($year % 4) == 0)
         ? 1
         : 0));

使用这种格式化方式,可以更简洁地编写代码,并使其更易读:

//--------------Test expression-----Result
$isLeapYear = (($year % 400) == 0) ? 1 :
              ((($year % 100) == 0)? 0 :
              ((($year % 4) == 0)  ? 1 :
                                     0)); // Default result

1

我最近制定的一个经验法则是判断是否应该使用三元运算符:

  • 如果你的代码需要在两个不同的值之间进行选择,那么可以使用三元运算符。
  • 如果你的代码需要在两条不同的代码路径之间进行选择,那么应该使用 if 语句。

同时,请对你的代码读者友好一点。如果你嵌套了多个三元运算符,请格式化代码以使嵌套关系明显可见。


1

0

我喜欢在某些情况下使用运算符,但我认为有些人倾向于过度使用它,这可能会使代码更难阅读。

最近我在修改一些开源代码时偶然发现了这行代码。

其中

    (active == null ? true :
    ((bool)active ? p.active : !p.active)) &&...

不要使用

where ( active == null || p.active == active) &&...

我想知道在这种情况下,三元运算符的使用是否会给LINQ语句增加额外的开销。


0
我同意这里许多帖子的观点。只要正确使用三元运算符且不会引入歧义(公平地说,任何运算符/结构都可以这样说),它就是完全有效的。
我经常在嵌入式代码中使用三元运算符来澄清我的代码正在做什么。看下面这个(为了清晰起见而过度简化)代码示例:
int direction = read_or_write(io_command);

// Send an I/O
io_command.size = (direction==WRITE) ? (32 * 1024) : (128 * 1024);
io_command.data = &buffer;
dispatch_request(io_command);

代码片段 2:

int direction = read_or_write(io_command);

// Send an I/O
if (direction == WRITE) {
    io_command.size = (32 * 1024);
    io_command.data = &buffer;
    dispatch_request(io_command);
} else {
    io_command.size = (128 * 1024);
    io_command.data = &buffer;
    dispatch_request(io_command);
}

在这里,我正在分派输入或输出请求。无论请求是读取还是写入,过程都是相同的,只有默认的I/O大小会改变。在第一个示例中,我使用三元运算符来明确表明该过程相同,并且size字段根据I/O方向获得不同的值。在第二个示例中,很难立即清楚两种情况的算法是相同的(特别是当代码比三行长得多时)。第二个示例将更难以保持公共代码同步。在这里,三元运算符更好地表达了代码的大致并行性质。

三元运算符还有另一个优点(虽然通常只在嵌入式软件中成为问题)。某些编译器只能在代码没有“嵌套”超过一定深度时才能执行某些优化(这意味着在函数内,每次进入if、loop或switch语句时增加嵌套深度1,离开时减少1)。偶尔地,使用三元运算符可以最小化需要放在条件中的代码量(有时甚至使编译器可以优化掉条件),并减少代码的嵌套深度。在某些情况下,我能够使用三元运算符重新构造一些逻辑(就像我的上面的例子一样),并将函数的嵌套深度降低到足以让编译器对其执行额外的优化步骤。诚然,这是一个相当狭窄的用例,但我觉得还是值得一提的。

1
为什么不把 io_command.data = &buffer; 和 dispatch_request(io_command); 移到 if-else 语句之外呢?把它们放在后面就可以减少代码重复了。 - EpsilonVector
这不公平。最后两个语句不依赖于条件,因此不应该在“if”语句中。 - Peter Mortensen

0
让代码变小并不总是意味着更容易解析。这取决于语言。
例如,在PHP中,空格和换行符是被鼓励的,因为PHP的词法分析器首先将代码分成以换行符开头的位,然后再是空格。所以我认为除非使用更少的空格,否则不会出现性能问题。
不好的例子:
($var)?1:0;

好的:

($var) ? 1 : 0;

看起来不是一个大问题,但在 PHP 中词法分析代码时,空格是必须的。而且,这样读起来也更好。


你有这些说法的来源吗?例如:“PHP的词法分析器首先将代码分解成以换行符和空格为起点的位”。PHP没有真正的解析器吗?不同种类的PHP代码格式化对性能的实际影响是什么? - Peter Mortensen

0

如果你试图减少代码行数或重构代码,那就去做吧。

如果你关心下一个程序员需要花费额外的0.1毫秒来理解表达式,那么无论如何都要去做。


0
我非常喜欢它。当我使用它时,我会像一个 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) );
对我来说,这很容易阅读。它还使得添加子情况或更改现有情况变得容易。

1
我以为我是一个超级粉丝,直到我看到那个例子。那需要一些适应时间。我只用它们来写一行代码,而不是块代码。 - Michael Haren
2
只需要去买一个Lisp,你这个不敢出柜的同性恋。 - niXar
这看起来很糟糕。而我认为自己是运算符的粉丝。 - EpsilonVector

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接