例如,您是否希望使用这种单行代码

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;
}


或涉及多个返回语句的if / else解决方案?

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

评论

它的这种特殊用法是:)

谁编码的,四个数字的中位数是什么样的?还是五个?

更正确的名称是“条件运算符”。它恰好是使用中最常见的三元运算符。

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

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

#1 楼


三元运算符是邪恶的吗?


不,这是一种祝福。


什么时候是?:合适吗?


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


,什么时候不行?


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


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

评论


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

–sange
2010-12-20在16:44



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

–琼斯医生
2010-12-20 at 16:48

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

–adamk
2010-12-20 17:55



@adamk:为邪恶而尝试:myVar = someExpression吗?假:真;

–迪恩·哈丁(Dean Harding)
2010-12-20 22:00



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

– fredoverflow
2010-12-21 10:42

#2 楼

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

评论


这可能被认为过于简化,但这是一个很容易遵循的指南,并且在大多数情况下都可以使用。

–艾伦·皮尔斯(Alan Pearce)
2010-12-21 15:08



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

–皮奥韦赞
2014年1月28日19:06

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

–candied_orange
14年8月29日在3:00

#3 楼

什么时候是?:适当的


什么时候使您的代码更简明易懂。

什么时候不?


什么时候使代码不可读。
如果要这样做,只是为了请一个重构工具,例如ReSharper而不是必须维护代码的人。

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

评论


这个值得很多赞扬!

–皮奥韦赞
2014年1月28日在19:07

#4 楼

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

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

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


vs

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


评论


太酷了。没想到。

–宫阪丽
2010-12-21 14:14

+1 @Benjol:我想指出同样的事情(在F#中,一切都是表达式,包括if / elif / else)。我也像您的示例中一样使用三元数,到目前为止还没有被发现:)。我一直在用Javascript做的另一件事,也许是在上面,是:var res = function(){switch(input){情况1:返回“ 1”;情况二:返回“ 2”; ...}}()将开关模拟为表达式。

–斯蒂芬·斯文森(Stephen Swensen)
2010-12-21 15:05

@Stephen,我本打算使用表达和声明一词,但我一直担心我会以错误的方式弄乱它们,然后自欺欺人:)

– Benjol
2010-12-22 6:02

@Benjol:我知道你的意思!

–斯蒂芬·斯文森(Stephen Swensen)
2010-12-22 13:16

在C和C ++中对初始化以后无法更改的const变量也很有用。

– David Thornley
2011年1月25日19:53

#5 楼

有效使用的示例(IMHO):

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


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

评论


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

– Anon。
2010-12-20 20:23

这是我的经验法则:1级嵌套(在某些情况下)。

–奥利弗·韦勒
2010-12-20 22:56

#6 楼

绝对不是邪恶的。实际上,它是纯粹的,而if-then-else并非如此。

在Haskell,F#,ML等功能语言中,if-then-else语句被认为是邪恶的。 br />
这样做的原因是,任何诸如命令式if-then-else语句之类的“动作”都需要您将变量声明与其定义分开,并将状态引入函数。

例如,在以下代码中:

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


vs.

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


第一个具有除了更短之外,还有两个优点:




x是一个常数,因此引入错误的机会要少得多,并且有可能以第二种不可能的方式进行优化。
通过表达式可以清楚地表明类型,因此编译器可以毫不费力地推断出x必须是Parity类型。

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

评论


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

–斯蒂芬·斯文森(Stephen Swensen)
2010-12-21 15:10

#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)
2011-1-25在20:36



#8 楼

VS.NET中的ReSharper有时建议用if...else运算符替换?:

ReSharper似乎仅在条件/块低于特定复杂度级别时才建议使用,否则坚持使用if...else。 >

评论


我喜欢ReSharper的另一个很棒的功能

–匿名类型
2010-12-20 21:39

#9 楼


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

如果我正在寻找开放源代码库来解决特定问题,并且看到了原始海报的三元代码等代码该库的候选人中的操作员,在我脑海中会响起警钟,我会开始考虑继续进行其他项目的借鉴。

#10 楼

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

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

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


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

#11 楼

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




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

printf("I see %d evil construct%s in this program\n", n, n == 1 ? "" : "s");


在您的特定示例中,三进制几乎是免费的,因为它是return下的顶级表达式。您可以将条件提升到语句级别,而无需重复除return关键字以外的任何内容。

N.B.没有什么能让这种特定的中值算法易于阅读。

评论


很难被人们理解,以至于无法使其变得更好。 printf(“我在该程序中看到%d邪恶的构造%s \ n”,n,“ s”,除非(n == 1)“ s”);

–起搏器
2015年2月6日在13:48

#12 楼

这是邪恶的时候的一个例子:

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;


三元数没有什么噪音。我想知道三元捷径是否;所有表达式都得到求值?

评论


您的第二个示例让我想起了如果(x!= 0)x = 0; ...

– fredoverflow
2011年1月25日19:55

#13 楼

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

if是一条语句。 (test ? a : b)是一个表达式。它们不是一回事。

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

三元表达式的一个价值在于它使您能够处理正确和错误的情况。 if语句不需要。

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

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

评论


我可以暗示犯罪嫌疑人是谁吗?只是为了提高我对该领域的了解。

– mlvljr
2011年1月26日上午10:52

@mlvljr:我可能是错的,所以最好不要。

–迈克·邓拉维(Mike Dunlavey)
2011-1-26在16:52

#14 楼

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

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

#15 楼

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

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

#16 楼

可以将其重新格式化为与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;
}


但是问题是我不确定我是否正确地使用了缩进代表实际情况。 :-)

评论


+1我认为这比等效的if-else结构要好得多。关键是格式。

–Orbling
2010-12-20 22:13

如果我在代码库中看到此内容,我将认真考虑寻找一份新工作。

–尼克·拉森(Nick Larsen)
2010-12-21 14:50

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

–马克·赫德
2011-1-26的2:32

只有另一种意外的自动格式设置才能消除所有缩进,并有时间进行另一轮重组-效率很高:)

– nawfal
13年4月23日在19:43



#17 楼

我可以说吗?我无法发现这种三元运算的特殊应用是有害的:


它执行的操作非常琐碎,一旦陷入低谷,几乎不可能出现一些错误出
函数名称中清楚地说明了它的作用;
采用> 1行表示如此明显的内容,以至于将来不会明显改善(除非魔术中位数算法直到现在还没有被发现) )。

请怜悯,我的声誉已经可怜了。

#18 楼

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

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


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

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


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

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


在最底层,这是最基本的DRY(不要重复自己)原则。如果只能指定一次$foo,请这样做。

#19 楼

如果...则...否则倾向于强调条件,因此不强调要有条件地进行操作。

三元运算符则相反,它倾向于隐藏条件,因此很有用当操作完成比条件本身更重要时。

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

#20 楼


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


我认为,为一群齐心协力的人发展时,没有问题,但必须与人打交道可以处理不同级别的代码,这种单行代码只会向代码引入更复杂的级别。因此,我对此事的政策是:清楚代码,不要解释,而不是简短代码,解释123123次。


应该向初学者讲授还是隐藏? br />

我不应该向初学者学习,而更希望他们在需要时弄清楚,因此仅在必要时使用,而不是在每次您需要if时使用。

#21 楼

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

A = x == y ? B : C;


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

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


即使如此,过深的嵌套也可能导致可读性问题,但至少A)凡是完成编程的人都可以弄清楚一个简单的问题,以及B)了解它的人可以轻松处理更深层次的嵌套。 OTOH,我还要注意,例如在LISP中,一个cond很像一个三元语句-不是一组语句,而是一个表达式可以产生一个值(然后,大多数LISP就是这样。 ..)

评论


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

–杰里米·海勒(Jeremy Heiler)
2011年1月25日在20:22

@杰里米:虽然有些人认为父母很有用,但即使充其量也没有太大帮助。嵌套多于两个,您仍然需要小心地缩进(至少是最小的数量),以使事情整齐。毫无疑问,最终在Algol中也会发生同样的事情,但是我从来没有说过会像在C中经常遇到的那样出现问题。

–杰里·科芬(Jerry Coffin)
2011年1月25日20:28



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

–杰里米·海勒(Jeremy Heiler)
2011年1月25日在20:31

#22 楼

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

#23 楼

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


如果没有获得性能上的提高,请不要使用它;它会影响代码的可读性。
只使用一次就不要嵌套。
调试起来比较困难。

应该教给初学者还是对初学者隐藏它?

Dosn没关系,但不应有意将其隐藏起来,因为对于初学者而言,学习起来并不复杂。

#24 楼

在您的示例中:

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-25在20:58

也许吧,但这是清晰的。

–克里斯托弗·马汉(Christopher Mahan)
2011-1-25 21:29

#25 楼

对于const

const int nLegs  = isChicken ? 2: 4 ;

也很有必要

评论


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

– nawfal
13年4月23日在19:48



@nawfal-如果在运行时之前不知道isChicken

–马丁·贝克特(Martin Beckett)
13年4月23日在22:47

是的,那是什么。我认为const在某些语言中是如此。在C#中,const应该始终是编译时的已知值。这意味着const int nLegs = isChicken吗? 2:4;不会工作,但const int nLegs = true吗? 2:4;将

– nawfal
13年4月24日在4:04