三元运算符被认为有害吗?[关闭]


79

例如,您是否更喜欢这种单线

int median(int a, int b, int c) {
    return (a<b) ? (b<c) ? b : (a<c) ? c : a : (a<c) ? a : (b<c) ? c : b;
}

还是涉及多个return语句的if / else解决方案?

什么时候?:合适,什么时候不合适?应该向初学者讲授还是隐藏起来?


221
它的这种特殊用法是:)
karmajunkie 2010年

6
谁编码的,四个数字的中位数是什么样的?还是五个?
梅森惠勒

3
更正确的名称是“条件运算符”。它恰好是使用中最常见的三元运算符。
艾伦·皮尔斯

1
这是两年多以前在stackoverflow上提出的。我们现在要重新询问这里的一切吗?stackoverflow.com/questions/160218/to-ternary-or-not-to-ternary
webbiedave 2010年

3
我很惊讶为什么这样的问题不断出现。答案始终是“一切可行且可读”。-最后一个同样重要。
Apoorv Khurasia

Answers:


234

三元运算符是邪恶的吗?

不,这是福气。

什么时候?:合适?

当事情变得如此简单时,您不想浪费很多行。

什么时候不呢?

就像您的示例一样,当代码的可读性和清晰度受到影响并且由于注意力不足而导致错误的可能性增加时,例如,许多链接的运算符也是如此。


石蕊测试是当您开始怀疑您的代码从长远来看是否易于阅读和可维护时。那不要做


23
当代码的可读性和清晰度受到损害时,为 +1 就像您的示例一样,拥有许多连锁运营商。 该示例比等价的if / else所需的时间更长。
2010年

23
+1精彩的解释!开发人员不会倾向于意识到某些事情是判断力,他们希望一切都是黑白的。它让我发疯。我遇到过很多人认为“ X是邪恶的,永远不要使用它”。我更喜欢“ X很棒,如果您将X用于其擅长的领域”。
琼斯医生2010年

54
如果使用它也是邪恶的:myVar =(someExpression)?真假; 啊!
adamk 2010年

20
@adamk:试试这个作恶:myVar = someExpression ? false : true;
Dean Harding 2010年

8
怎么样(someExpression ? var1 : var2)++:-)
fredoverflow

50

我认为未嵌套的三元运算符(例如,仅使用一次的语句)可以,但是如果嵌套多个,则很难理解。


3
这可能被认为过于简化,但这是一个非常容易遵循的指南,并且在大多数情况下都可以使用。
艾伦·皮尔斯

2
实际上,这是我的经验法则:您永远不要嵌套它们,否则用if / else代替它们,这样会更清楚。
Piovezan

如果您要使用它们并嵌套它们,那么出于对人类的热爱,请使用括号和空白使其可读。If-else可能很难做到。通过编写编译器可以读取但人类无法读取的东西来抵制炫耀自己的“智能”欲望。总有一天,你将成为无法做到的人。
candied_orange 2014年

24

什么时候是?

  • 当它使您的代码更简洁易读时。

什么时候不呢?

  • 当它使您的代码不可读时。
  • 如果这样做是为了取悦ReSharper之类的重构工具,而不是需要维护代码的人

如果您在三元表达式中有任何逻辑或函数调用,那么看起来就很恐怖。


这个值得很多赞扬!
Piovezan

22

(我认为)没有人指出的一个区别是if-else不能返回值,而三元运算符可以。

来自F#,我有时喜欢使用三元运算符来模拟模式匹配。

match val with
| A -> 1
| B -> 3
| _ -> 0

return val == A ? 1 : 
       val == B ? 3 : 
       0;

太酷了。没想到。
宫阪丽

+1 @Benjol:我要指出同一件事(在F#中,一切都是表达式,包括if / elif / else)。我也像您的示例中那样使用三元数,到目前为止也尚未发现:)。我一直在用Javascript做的另一件事,也许是在上面,是:var res = function() {switch(input) {case 1: return "1"; case 2: return "2"; ...}}()将开关模拟为表达式。
斯蒂芬·斯文森

@Stephen,我要用这些词expressionstatement但我一直担心我会以错误的方式弄错它们,然后自欺欺人:)
Benjol 2010年

@Benjol:我知道你的意思!
斯蒂芬·斯文森

6
在C和C ++中对初始化const以后不能更改的变量也很有用。
David Thornley

13

有效使用的示例(IMHO):

printf("Success in %d %s\n", nr_of_tries, (nr_of_tries == 1 ? "try" : "tries"));

与具有2个不同的打印语句相比,这将导致更具可读性的代码。嵌套的示例取决于:(可理解?是:否)


11
请记住,如果执行此操作,对于必须本地化您的应用程序的人来说,这简直是地狱。当然,如果那不是问题,请继续。
Anon。

1
这是我的经验法则:1级嵌套(在某些情况下)。
奥利弗·韦勒

7

绝对不是邪恶的。实际上,它是函数,而if-then-else不是。

在Haskell,F#,ML等功能语言中,if-then-else语句被认为是邪恶的。

这样做的原因是,诸如命令式if-then-else语句之类的任何“动作”都要求您将变量声明与其定义分开,并将状态引入到函数中。

例如,在以下代码中:

const var x = n % 3 == 1
    ? Parity.Even
    : Parity.Odd;

Parity x;
if (n % 3 == 1)
    x = Parity.Even;
else
    x = Parity.Odd;

第一个优点是较短,但有两个优点:

  1. x 是一个常量,因此引入错误的机会要少得多,并且有可能以第二种不可能的方式进行优化。
  2. 通过表达式可以明确说明类型,因此编译器可以轻松推断出x需要的类型Parity

令人困惑的是,在功能语言中,三元运算符通常称为if-then-else。在Haskell中,您可能会说x = if n mod 3 == 1 then Odd else Even


是的,这也是@Benjol提出的重点。请参阅我对他的回答的评论,以有趣的方式将switch语句模拟为Java表达式。
斯蒂芬·斯文森

7

这种特殊的表情使我的眼睛受伤;我会抨击团队中使用它的任何开发人员,因为它无法维护。

三元运算符使用得当并不是邪恶的。他们甚至不必是一行。格式正确的长篇文章非常清晰易懂:

return
      ( 'a' == $s ) ? 1
    : ( 'b' == $s ) ? 2
    : ( 'c' == $s ) ? 3
    :                 4;

我比同等的if / then / else链更好:

if ( 'a' == $s ) {
    $retval = 1;
}
elsif ( 'b' == $s ) {
    $retval = 2;
}
elsif ( 'c' == $s ) {
    $retval = 3;
}
else {
    $retval = 4;
}

return $retval;

我将其重新格式化为:

if    ( 'a' == $s ) { $retval = 1; }
elsif ( 'b' == $s ) { $retval = 2; }
elsif ( 'c' == $s ) { $retval = 3; }
else                { $retval = 4; }

return $retval;

条件和分配是否允许轻松对齐。我仍然更喜欢三元版本,因为它更短,并且在条件和赋值周围没有太多噪音。


为什么我不能在评论中加入换行符?啊!
Christopher Mahan


2

可以将其重新格式化为与if / else组合一样美观:

int median(int a, int b, int c)
{
    return
        (a<b)
        ?
            (b<c)
            ? b
            :
                (a<c)
                ? c
                : a
        :
            (a<c)
            ? a
            :
                (b<c)
                ? c
                : b;
}

但是问题是我不确定我是否正确地表示了将要发生的缩进。:-)


3
我认为+1比同等if-else结构好得多。关键是格式。
2010年

13
如果我在代码库中看到了这一点,我将认真考虑寻找一份新工作。
尼克·拉尔森

+1并非针对此实际缩进,但这是示例的最佳解决方案。
马克·赫德

2
只有另一种意外的自动格式化会消除所有的缩进,并有时间进行另一轮重组-效率
很高

2

三元运算符绝非邪恶,而是天赐之物。

  • 当您想在嵌套表达式中做出决定时,它非常有用。经典示例是一个函数调用:

    printf("I see %d evil construct%s in this program\n", n, n == 1 ? "" : "s");
    
  • 在您的特定示例中,三进制几乎是免费的,因为它是。下的顶级表达式return。您可以将条件提升到语句级别,而无需复制return关键字以外的任何内容。

注意:没有任何东西会使中位数的特定算法易于阅读。


很难被人们理解,以至于无法使其变得更好。printf("I see %d evil construct%s in this program\n", n, "s" unless (n == 1) "s");
Pacerier,2015年

2
  1. 除了“邪恶”的论点外,根据我的经验,我发现程序员使用三元运算符与他或她的整个代码库难以阅读,遵循和维护(如果不是完全没有记载)的可能性之间存在高度相关性。如果程序员比能够理解他或她的代码的人更关心节省1-2个字符的行,那么理解三元语句的任何细微混乱通常就是冰山一角。

  2. 三元运算符像s ** t一样吸引魔术数,也吸引苍蝇。

如果我正在寻找一个开放源代码库来解决特定问题,并且在该库的候选对象中看到诸如原始海报的三元运算符之类的代码,警告之钟就会开始响起,我将开始考虑继续前进借给其他项目。


2

邪恶的时候的一个例子:

oldValue = newValue >= 0 ? newValue : oldValue;

令人困惑和浪费。编译器可以优化第二个表达式(oldValue = oldValue),但是为什么编码器首先这样做呢?

另一个笨拙:

thingie = otherThingie != null ? otherThingie : null;

有些人并不意味着要成为编码员...

格雷格说等效的if语句是“嘈杂的”。这是如果您吵闹地写的话。但是,如果可以写成这样:

if ('a' == $s) return 1;
if ('b' == $s) return 2;
if ('c' == $s) return 3;
return 4;

没有什么比三元组更嘈杂了。我想知道三元捷径是否;是否所有表达式都得到求值?


您的第二个示例使我想起了if (x != 0) x = 0;……
fredoverflow 2011年

2

邪恶?看,它们只是不同。

if是一个声明。(test ? a : b)是一个表达。他们不是一回事。

存在表示值的表达式。存在执行操作的语句。表达式可以出现在语句内部,反之亦然。因此,您可以在其他表达式中使用三元表达式,例如用于求和项中的项或用于方法的参数等。你不,但你可以,如果你想。没有错。有人可能会说这很邪恶,但这是他们的意见。

三元表达式的一个值是它使您能够处理正确和错误的情况。if声明没有。

如果您担心可读性,可以将它们格式化为可读格式。

不知何故,“邪恶的”爬上了编程词汇表。我很想知道谁先丢了它。(实际上,我有一个嫌疑人-他在麻省理工学院。)我宁愿我们有客观的理由来进行这一领域的价值判断,而不仅仅是人们的品味和呼唤。


1
我可以暗示嫌疑人是谁吗?只是为了提高我对该领域的知识。
mlvljr 2011年

1
@mlvljr:我可能是错的,所以最好不要这样做。
Mike Dunlavey

1

它有一个地方。我曾在许多公司中工作,这些公司的开发人员的技能水平从可怕到巫师不等。由于必须维护代码,而且我不会永远存在,所以我尝试编写一些东西,使它看起来像是属于那里的(不用我的姓名缩写看注释,对于您来说,很少见查看我工作过的代码,看看我在哪里进行了更改),并且技能比我自己低的人可以维护它。

虽然三元运算符看起来很灵活,但我的经验是代码行将几乎无法维护。在我现在的老板那里,我们有已经出货近20年的产品。我不会在任何地方使用该示例。


1

我认为三元运算符不是邪恶的。

不过,这是让我感到困惑的陷阱。我是许多(10+)的C程序员,在1990年代后期,我进入了基于Web的应用程序编程。作为一名Web程序员,我很快遇到了PHP,PHP也具有三元运算符。我在一个PHP程序中遇到一个错误,最后我使用嵌套的三元运算符跟踪了一行代码。事实证明,PHP三元运算符从左到右关联,但是C三元运算符(我曾经用来)从右到左关联。


1

任何使您的代码更丑陋的东西都是邪恶的。

如果使用三元代码使代码更整洁,请确保使用它。有时,像php一样,最好进行内联替换,例如

"Hello ".($Male?"Mr":"Ms")." $Name

这样可以节省几行,而且很清楚,但是您的示例至少需要更好的格式才能清楚,并且三元数对于多行并不是真正的好方法,那么您最好使用if / else。


1

我可以说吗?我不能设法找到三元操作的这种特殊应用的邪恶

  1. 它执行的操作是非常琐碎的,一旦您陷入低谷,就几乎不可能出现一些错误;
  2. 函数名称中明确说明了它的作用;
  3. 采取> 1行表示如此明显的内容,以至于将来显然不会得到改善(除非直到现在为止还没有发现不可思议的中值算法)。

请仁慈,我的声誉已经很可怜了。


1

最大的胜利:表明只有一个行动目标。

if ( $is_whatever )
    $foo = 'A';
else
    $foo = 'B';

您可以遵循两条代码路径,并且读者必须仔细阅读以了解设置了哪些两个变量。在这种情况下,它只是一个变量,但是读者有更多的阅读要弄清楚。毕竟,可能是这样的:

if ( $is_whatever )
    $foo = 'A';
else
    $bar = 'B';

使用三元运算符,很明显只设置了一个变量。

$foo = $is_whatever ? 'A' : 'B';

从最低方面讲,这是最基本的DRY(请勿重复自己)原则。如果只能指定$foo一次,请这样做。


0

如果...,则...否则趋向于强调条件,因此不再强调有条件地执行的操作。

三元运算符则相反,它倾向于隐藏条件,因此在执行操作比条件本身更重要时非常有用。

在某些语言中,有一个技术上的小问题,即它们不是很可互换,因为一个是语句,一个是表达式,例如在C ++中有条件地初始化const。


0

什么时候合适,什么时候不合适?

我认为,当为一群齐心协力的人进行开发时,没有问题,但是当您必须与处理不同级别的人员打交道时,这种单行代码只会在代码中引入更复杂的层次。因此,我对此事的政策是:代码清晰而不说明,而不是代码简短说明123123次。

应该向初学者讲授还是隐藏起来?

我不应该教给初学者,而宁愿他们在需求出现时弄清楚它,因此仅在必要时使用它,而不是在您每次需要的时候都使用它。


0

IMO,运算符本身并不邪恶,但是在C(和C ++)中用于该运算符的语法过于简洁。IMO,Algol 60做得更好,所以像这样:

A = x == y ? B : C;

看起来会更像这样(但通常会遵循类似C的语法):

A = if (x==y) B else C;

即使这样,过深的嵌套也可能导致可读性问题,但是至少A)完全完成编程的任何人都可以算出一个简单的嵌套,以及B)理解它的人可以轻松地处理更深层次的嵌套。OTOH,我还要注意,例如在LISP中,a cond非常类似于三元语句-不是一组语句,而是单个表达式会产生一个值(然后,大多数LISP都是这样。 )


为什么不这样做只是为了提高可读性?A = (x==y) ? B : C
杰里米·海勒

@杰里米:虽然有些人认为父母有用,但即使充其量也没有多大帮助。嵌套多于两个深度,您仍然需要小心地缩进(至少是最小的数量),以使事物整齐。毫无疑问,同样会在最终陵发生,但我从来不说出现在它的问题就像我经常用C做...
杰里棺材

我只是假设每个人都同意嵌套三元运算符是不好的。我是在专门谈论您提供的示例。特别是,在大多数语言中,第一个可以更像第二个。
Jeremy Heiler 2011年

0

定期编写600-1200行方法的商店不应告诉我三元是“很难理解的”。任何定期允许五个条件评估代码分支的商店都不应告诉我,三元数的具体总结条件“很难阅读”。


0

什么时候是?:合适,什么时候不合适?

  • 如果您没有获得性能提升,请不要使用它。它会影响代码的可读性。
  • 使用一次,不要嵌套。
  • 很难调试。

应该向初学者讲授还是隐藏起来?

没关系,但是不应故意隐藏它,因为对于初学者来说学习起来并不复杂。


-2

在您的示例中:

def median(a, b, c):
    if a < b < c: return b
    if a < c < b: return c
    if b < a < c: return a
    if b < c < a: return c
    if c < a < b: return a
    if c < b < a: return b

读起来很简单,很明显。<<之间的变量是返回值。

更新资料

相同,但是更少的代码行。我认为仍然很简单。

def median(a, b, c):
    if b<a<c or c<a<b: return a
    if a<b<c or c<b<a: return b
    if a<c<b or b<c<a: return c

在最坏的情况下,这需要进行12次比较...
fredoverflow 2011年

1
也许吧,但这是清晰的。
Christopher Mahan

-2

对于const也有必要

const int nLegs  = isChicken ? 2: 4 ;

奇怪。我认为它是C ++之类的。我以为const一直都是编译时间常数(如C#)
nawfal 2013年

@nawfal-如果您直到运行时都不知道isChicken
Martin Beckett

是的,那是什么。我认为const某些语言是这样。在C#中const应该始终是编译时的已知值。这意味着const int nLegs = isChicken ? 2: 4 ;不会工作,但是const int nLegs = true ? 2: 4 ;
nawfal
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.