要三元还是不三元?[关闭]


186

我本人是三元运算符的拥护者:()?:; 我确实意识到它有它的位置,但是我遇到了许多程序员,他们完全反对使用它,有些人经常使用它。

您对此有何感想?您看到过使用它有趣的代码吗?


22
倡导某事物的人是支持该事物的人。
道格·麦克林

8
清楚时使用它,避免混淆时避免使用它。那是个判断电话。它可以使代码更具可读性,但仅适用于简单表达式。一直尝试使用它就像不懈地避免使用它一样具有威胁。
亚伯2009年

3
实际上,它是条件运算符。一个接近重复的问题是stackoverflow.com/questions/725973/…
丹尼尔·达拉纳斯

我有时会使用x = x if x else y它,但是后来问了一下,并在其他人的帮助下意识到,它实际上只是减少到x = x或y(stackoverflow.com/questions/18199381/self-referencing-ternary/…
S

4
诸如此类的问题不应被认为不是建设性的。
Ungeheuer

Answers:


243

将其用于简单表达式

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;

80
完全同意前几条陈述,但完全不同意“提高可读性”的示例。如果要进行多行处理,为什么不使用if语句呢?
乔·菲利普斯

3
仅仅因为if,对于简单的决策而言,它的含义更为冗长:int a = 0; if(b> 10)a =某个值; 否则a = another_value; 你喜欢哪个?
marcospereira

44
@ d03boy:因为if语句就是那条语句,并且当您想要的只是一个表达式时不会执行。
falstro 2010年

2
@roe在某些语言中是表达式(例如在Scala中),因此val x = if(true) 0 else 1完全合法
om-nom-nom

5
以@ om-nom-nom为例,这将使其成为一个if表达式,而不是一个if语句,并且实质上与?:-运算符相同。
falstro

141

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


44
这是我听过的反对三元运算符的最佳论据。我不赞成“不可读”的论点(在我看来,这听起来像是人们懒于适应),但这实际上是有实质意义的。
EpsilonVector

80

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

我不知道这是怎么回事:

int count = (condition) ? 1 : 0;

比这更难:

int count;

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

编辑-

我认为三元运算符使所有内容都比其他运算符更简单,更整洁。


5
当变量为常数时,三元初始化在D或C ++中更加有用。例如 const int count = ...;
deft_code 2011年

好吧,您对if/else那里不需要的牙套有点误解。
bobobobo

1
此外,在这种情况下,如果conditionbool ,你可能只是这样做 int count = condition;
bobobobo

1
@bobobobo这个if/else大括号是大多数程序员如何将改写三元..
安德烈·菲格雷多

1
@bobobobo如果不使用大括号只是在问麻烦。对于某人而言,添加行太容易了,但是忘记了它随后需要大括号来执行他们期望的操作(将额外的行作为块的一部分执行):stackoverflow.com/a/381274/377225
George Marian

43

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

我倾向于在C中更多地使用它们,因为b / 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"; }

在这样的作业中,我发现重构和清晰的工作要少得多。

另一方面,当我在红宝石中工作时,我更倾向于使用if...else...end它,因为它也是一种表达方式。

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

(尽管,坦白地说,对于这种简单的事情,我仍然可以使用三元运算符)。


2
我喜欢您的第一个示例-以前从未想过将它们链接在一起。感谢分享。=)
Erik Forbes

5
+1实际上是我最近开始做的。我什至可以替换掉switch语句。
戴维

1
很好的例子@rampion。
ylluminate

39

三元?:运算符只是程序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结构上使用赞美语,因此您的第二个示例缺少一些恕我直言。
克里斯(Kris)

是的,它是功能正常的。这就像一个具有单个布尔参数的微小函数,并返回您想要的任何类型!实际上是个整洁的操作员。
bobobobo 2012年

24

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

  • 仅评估1个表达式-因此foo = (bar > baz) ? true : false,但不评估foo = (bar > baz && lotto && someArray.Contains(someValue)) ? true : false
  • 如果我将其用于显示逻辑,例如 <%= (foo) ? "Yes" : "No" %>
  • 仅将其真正用于分配;永不流动逻辑(因此永不(foo) ? FooIsTrue(foo) : FooIsALie(foo) 三元流逻辑本身就是一个谎言,忽略最后一点。

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


好的指导方针和好的例子!
尼克

我认为大多数语言不会让您将其用于流程逻辑,因此您无法执行(foo)?True(foo):False(foo); 除非是作业。
亨利

糟糕,是的,我是对的。
基思·威廉姆斯

21
您的前两个示例确实很糟糕。比较的结果已经是布尔值,因此您的三元运算符没有用,只会使代码复杂化。
Trillian

1
@Trillian +1是,应该分配其他任务。 foo = (bar > baz);更简单
Eric

18

像许多意见问题一样,答案不可避免地是: 它取决于

对于类似:

return x ? "Yes" : "No";

我觉得很多更简洁(对我来说解析起来更快):

if (x) {
    return "Yes";
} else {
    return "No";
}

现在,如果您的条件表达式很复杂,那么三元运算不是一个好的选择。就像是:

x && y && z >= 10 && s.Length == 0 || !foo

对于三元运算符不是一个很好的选择。

顺便说一句,如果您是C程序员,那么GCC实际上具有扩展名,使您可以排除三元组的if-true部分,如下所示:

/* 'y' is a char * */
const char *x = y ? : "Not set";

将设置xy假设y不是NULL。好东西。


修复了轻微的语法和语法问题,Sean :-)代码的最后一位缺少y,“将x分配给y”的意思是“ y = x”,因此我更改了“将x设置为y”的位置。
paxdiablo,

@Pax:谢谢!我回滚了语法更改,因为我试图指出,使用GCC扩展,您不需要三进制的if-true部分。
肖恩·布莱特

抱歉,没有看到该段。不知道我是否同意这种说法,因为它允许人们编写无法使用ISO标准编译器进行编译的代码。不过,当GCC是最后一个站着的人时,这并不重要:-)
paxdiablo

毫无疑问,这是伏都教...谁不使用GCC?:D
肖恩·布莱特

14

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

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


问题是该语言占了99%,一个表达式可以用一个函数代替...而避免三元运算符的ppl甚至会更喜欢该解决方案。
PierreBdR

11

通过度量循环复杂性,使用if语句或三元运算符是等效的。因此,通过这种方法,答案是否定的,复杂度将与以前完全相同。

通过其他措施,例如可读性,可维护性和DRY(请勿重复),任何一种选择都可能比另一种更好。


10

我经常在不得不在构造函数中使用的地方(例如,新的.NET 3.5 LINQ to XML构造)使用它来定义可选参数为null时的默认值。

人为的例子:

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

或(感谢星号)

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()));
–'asterite

2
我喜欢您将其格式化以提高可读性,但很多人都不愿意。
08年

如果人类可读的源代码不可读,那么它有什么用呢?=)
Erik Forbes

9

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

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


1
我完全同意。没有任何隐藏或棘手的问题。
mmattax

2
这可能会导致可读性问题,尤其是在嵌套时。
David Thornley,2009年

我认为“ 非常难以阅读”有点宽容,但总的来说,我同意你的看法。这没有什么困难或神秘的。
EpsilonVector

8

(一天中的时间)

#define IF(x) x ?
#define ELSE :

然后,您可以将if-then-else用作表达式:

int b = IF(condition1)    res1
        ELSE IF(condition2)  res2
        ELSE IF(conditions3) res3
        ELSE res4;

7

我同意jmulder:不应将其代替 if其代替,但其用于返回表达式或表达式内部:

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

前者只是一个例子,应该使用更好的i18n复数支持!


第一个三进制不正确-您有一个:哪里?应该走:(n!= 1 “ s”:“”)
Erik Forbes

是的,谢谢指出!固定。
PhiLho's

6

如果您使用三元运算符进行简单的条件赋值,我认为很好。我已经看到它(甚至)用于控制程序流,甚至没有进行分配,我认为应该避免这种情况。在这些情况下,请使用if语句。


5

我认为应在需要时使用三元运算符。这显然是一个非常主观的选择,但是我发现一个简单的表达式(特别是作为返回表达式)比完整的测试要清晰得多。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中逻辑运算符的属性:它们是惰性的,如果最后一个值等于最终状态,则返回最后计算的值。


3
最后一行伤害了我的大脑……
埃里克·福布斯

5

我喜欢他们。我不知道为什么,但是当我使用三元表达式时,我感觉很酷。


5

我见过这样的野兽(实际上是更糟糕的,因为它是isValidDate,并且也逐月检查,但我不想烦恼想起整个事情):

isLeapYear =
    ((yyyy % 400) == 0)
    ? 1
    : ((yyyy % 100) == 0)
        ? 0
        : ((yyyy % 4) == 0)
            ? 1
            : 0;

显然,一系列if语句会更好(尽管它仍然比我曾经见过的宏版本更好)。

我不介意小事情:

reportedAge = (isFemale && (Age >= 21)) ? 21 + (Age - 21) / 3 : Age;

甚至有些棘手的事情,例如:

printf ("Deleted %d file%s\n", n, (n == 1) ? "" : "s");

您如何说一系列隔离 if语句更易读?我读过你的“野兽” 就好了reportedAge这个例子需要一些思考-可能是因为它比解决特定问题更重要isThisYearALeapYear
-Axeman

4

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

我建议创建一个宏以使其更具可读性,但是我敢肯定有人会提出一个可怕的极端案例(就像CPP一样)。


许多BASIC实现和变体都有一个IF函数来代替三元运算符。我在C中看到了一些定义为宏的代码库
。– Sparr

好吧,我在想函数式编程语言,但是可以。
Marcin

“制作一个宏使其更具可读性”,您真是个小丑!
niXar

4

我喜欢在调试代码中使用运算符来打印错误值,因此我不必一直查找它们。通常,我这样做是为了调试打印,一旦完成开发,这些打印就不会保留。

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


4

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

我喜欢避免冗长,但是当它使代码更容易上手时,我会选择冗长。

考虑:

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;

似乎只是将太多的信息压缩到了很小的空间中,而没有弄清楚发生了什么。每当我看到使用三元运算符时,我总会发现一个看起来更容易阅读的替代方法……然后再说一次,这是一个非常主观的观点,因此,如果您和您的同事发现三元运算符非常易读,那就去做吧。


1
那不是完全一样的东西。在第二个示例中,您将所有三个语句压缩为一行。那就是降低可读性的原因,而不是三元运算符。
ilitirit

相当公平,我已更新并加入了您的评论,但它仍然让我感到混乱……但这又是主观的……我不是说三元不可读,我是说它对我不可读(99 %的时间)
Mike Stone


3

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

before:

if(isheader)
    drawtext(x,y,WHITE,string);
else
    drawtext(x,y,BLUE,string);

after:

    drawtext(x,y,isheader==true?WHITE:BLUE,string);

当然,在大多数语言中,您也不需要该三元组的“ == true”部分。
Michael Haren

我意识到,尽管我倾向于为了使代码更具可读性,因为编译器应该将其优化为与没有== true时相同的东西
KPexEA,

您可能用任何语言都无法使用“ == true”
niXar,2009年

我很难决定是否投票。这个例子很好,但是== TRUE是我在别人的代码中看不到的。
PeterPerháč10年

3

正如其他人指出的那样,它们适合短时的简单条件。我特别喜欢它们作为默认值(类似于|| javascript和python中的用法),例如

int repCount = pRepCountIn ? *pRepCountIn : defaultRepCount;

另一个常见用途是在C ++中初始化引用。由于引用必须在同一条语句中声明和初始化,因此不能使用if语句。

SomeType& ref = pInput ? *pInput : somethingElse;

2
令人惊讶的是,这是首次提到初始化引用,这是少数几个不能使用“ if”代替?:的地方之一。(我想是因为这不是C ++特有的问题...)由于相同的原因,它们在构造函数初始化列表中也很有用。
j_random_hacker

2

我像GOTO一样对待三元运算符。它们有自己的位置,但是通常应避免使它们易于理解。


2

我最近看到了三元运算符的一种变体(很好),它使标准的“()?:”变体看起来像是清楚的典范:

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

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

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

请注意,这是Javascript,因此(谢天谢地)其他语言可能无法实现这种功能。


1
哇,太可怕了!想象将其中的两个嵌套在一起!我可以看到的唯一隐约有用的东西是,如果您有一个返回2元素数组的函数:var Name = getNames()[Gender =='m']; ...但是几乎没有可读性!
尼克

2

对于简单的情况,我喜欢使用它。实际上,作为功能或类似事物的参数,例如将其读/编码要容易得多。另外,为了避免出现新行,我希望保留所有if / else。

在我的书中,否定它是一个很大的禁忌。

因此,继续,对于单个if / else,我将使用三元运算符。在其他情况下,常规if / else if / else(或切换)


2

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

expr ?: default

如果该代码不为null,则该代码的计算结果为expr,如果为null,则其默认值为。从技术上讲,它并不是真正的三元运算符,但与它绝对相关,并且可以节省大量时间/键入代码。


是啊,我喜欢这一个,以及-这是??在C#中,空COALESCE操作:stackoverflow.com/questions/278703/...
贾罗德迪克森

2

对于像根据条件分配不同值之类的简单任务,它们很棒。当有更长的表达式(取决于条件)时,我不会使用它们。


2

有这么多答案说,这取决于。我发现,如果在快速浏览代码后看不到三元比较,则不应使用它。

作为附带的问题,我可能还要指出,由于在C语言中比较测试是一个陈述,因此它的存在实际上有点奇怪。在Icon中,if构造(就像大多数Icon一样)实际上是一个表达式。因此,您可以执行以下操作:

x[if y > 5 then 5 else y] := "Y" 

...我发现它比实习生比较运算符更具可读性。:-)

最近讨论了将?:操作符添加到Icon 的可能性,但是一些人正确地指出,由于这种方式,绝对没有必要if

这意味着,如果您可以使用C语言(或具有ternery运算符的任何其他语言)执行此操作,那么实际上您根本不需要ternery运算符。


2

如果您和您的同事了解他们的工作,并且他们不是成批地创建的,那么我认为他们会使代码的复杂度降低并且更易于阅读,因为代码很少。

我认为三元运算符使代码更难理解的唯一情况是一行中的行数超过3或4。大多数人都不记得它们是基于正确的优先级,当您拥有大量优先级时,这会使阅读代码成为噩梦。

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.