三元还是不三元?

我个人是三元运算符的拥护者:() ? :

我确实意识到它有它的位置,但我遇到过许多完全反对使用它的程序员,还有一些经常使用它。

你对它有什么感觉?你见过使用它的什么有趣的代码?

55088 次浏览

In my mind, it only makes sense to use the ternary operator in cases where an expression is needed.

In other cases, it seems like the ternary operator decreases clarity.

Well, the syntax for it is horrid. I find functional ifs very useful, and they often makes code more readable.

I would suggest making a macro to make it more readable, but I'm sure someone can come up with a horrible edge case (as there always is with C++).

I use it quite often in places where I'm constrained to work in a constructor - for example, the new .NET 3.5 LINQ to XML constructs - to define default values when an optional parameter is null.

Contrived example:

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

or (thanks asterite)

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

No matter whether you use the ternary operator or not, making sure your code is readable is the important thing. Any construct can be made unreadable.

I treat ternary operators a lot like GOTO. They have their place, but they are something which you should usually avoid to make the code easier to understand.

It makes debugging slightly more difficult since you can not place breakpoints on each of the sub expressions. I use it rarely.

I agree with jmulder: it shouldn't be used in place of a if, but it has its place for return expression or inside an expression:

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

The former is just an example, and better internationalisation and localisation support of plural should be used!

I use the ternary operator wherever I can, unless it makes the code extremely hard to read, but then that's usually just an indication that my code could use a little refactoring.

It always puzzles me how some people think the ternary operator is a "hidden" feature or is somewhat mysterious. It's one of the first things I learnt when I start programming in C, and I don't think it decreases readability at all. It's a natural part of the language.

I think the ternary operator should be used when needed. It is obviously a very subjective choice, but I find that a simple expression (specially as a return expression) is much clearer than a full test. Example in C/C++:

return (a>0)?a:0;

Compared to:

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

You also have the case where the solution is between the ternary operator and creating a function. For example in Python:

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

The alternative is:

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

It is needed enough that in Python (as an example), such an idiom could be seen regularly:

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

this line uses properties of the logical operators in Python: they are lazy and returns the last value computed if it is equal to the final state.

It's a question of style, really; the subconscious rules I tend to follow are:

  • Only evaluate 1 expression - so foo = (bar > baz) ? true : false, but NOT foo = (bar > baz && lotto && someArray.Contains(someValue)) ? true : false
  • If I'm using it for display logic, e.g. <%= (foo) ? "Yes" : "No" %>
  • Only really use it for assignment; never flow logic (so never (foo) ? FooIsTrue(foo) : FooIsALie(foo) ) Flow logic in ternary is itself a lie, ignore that last point.

I like it because it's concise and elegant for simple assignment operations.

I almost never use the ternary operator, because whenever I do use it, it always makes me think a lot more than I have to later when I try to maintain it.

I like to avoid verbosity, but when it makes the code a lot easier to pick up, I will go for the verbosity.

Consider:

String name = firstName;


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


name += " " + lastName;

Now, that is a bit verbose, but I find it a lot more readable than:

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

Or:

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

It just seems to compress too much information into too little space, without making it clear what's going on. Every time I saw the ternary operator used, I have always found an alternative that seemed much easier to read... then again, that is an extremely subjective opinion, so if you and your colleagues find ternary very readable, go for it.

I like using the operator in debug code to print error values so I don't have to look them up all the time. Usually I do this for debug prints that aren't going to remain once I'm done developing.

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

I'm a big fan of it ... when appropriate.

Stuff like this is great, and, personally, I don't find it too hard to read/understand:

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

I know that probably makes a lot of people cringe, though.

One thing to keep in mind when using it in PHP is how it works with a function that returns a reference.

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"

Use it for simple expressions only:

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

Don't chain or nest ternary operators as it hard to read and confusing:

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

Moreover, when using ternary operator, consider formatting the code in a way that improves readability:

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

I recently saw a variation on ternary operators (well, sort of) that make the standard "() ? :" variant seem to be a paragon of clarity:

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

or, to give a more tangible example:

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

Mind you, this is JavaScript, so things like that might not be possible in other languages (thankfully).

Only when:

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

KISS.

How would anyone win an obfuscated code contest without the ternary operator?!

I'm personally for using it, when appropriate, but I don't think I'd ever nest it. It's very useful, but it has a couple knocks against it in that it makes code harder to read and is in use in some other languages in other operations (like Groovy's null-check).

The ternary ?: operator is merely a functional equivalent of the procedural if construct. So as long as you are not using nested ?: expressions, the arguments for/against the functional representation of any operation applies here. But nesting ternary operations can result in code that is downright confusing (exercise for the reader: try writing a parser that will handle nested ternary conditionals and you will appreciate their complexity).

But there are plenty of situations where conservative use of the ?: operator can result in code that is actually easier to read than otherwise. For example:

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

Now compare that with this:

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

As the code is more compact, there is less syntactic noise, and by using the ternary operator judiciously (that is only in relation with the reverseOrder property) the end result isn't particularly terse.

For simple if cases, I like to use it. Actually it's much easier to read/code for instance as parameters for functions or things like that. Also to avoid the new line I like to keep with all my if/else.

Nesting it would be a big no-no in my book.

So, resuming, for a single if/else I'll use the ternary operator. For other cases, a regular if/else if/else (or switch).

Interesting anecdote: I have seen the optimizer weigh the ternary operator as less "heavy" for the purposes of inlining than the equivalent if. I noticed this with Microsoft compilers, but it could be more widespread.

In particular functions like this would inline:

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

But this wouldn't:

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

I typically use it in things like this:

before:


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


after:


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

I like it a lot. When I use it, I write it like an if-then-else: one line each for condition, true action, and false action. That way, I can nest them easily.

Example:

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

To me, this is reasonably easy to read. It also makes it easy to add subcases or change existing cases.

Chained I'm fine with - nested, not so much.

I tend to use them more in C simply because they're an if statement that has value, so it cuts down on unnecessary repetition or variables:

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

rather than

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

In assignments like this, I find it's less to refactor, and clearer.

When I'm working in ruby on the other hand, I'm more likely to use if...else...end because it's an expression too.

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

(Although, admittedly, for something this simple, I might just use the ternary operator anyway.)

I use and recommend ternaries to avoid code lines in situations where the logic is trivial.

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

In the above case I would choose a ternary, because it has less noise:

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

Likewise conditional return values are good candidates:

return ( piVal ) ? *piVal : *piDefVal;

I think compactness can improve readability which in turn helps to improve the code quality.

But readability always depends on the code's audience.

The readers must be able to understand the a ? b : c pattern without any mental effort. If you can not presume this, go for the long version.

I like Groovy's special case of the ternary operator, called the Elvis operator: ?:

expr ?: default

This code evaluates to expr if it's not null, and default if it is. Technically it's not really a ternary operator, but it's definitely related to it and saves a lot of time/typing.

If your ternary operator ends up taking the whole screen width, then I wouldn't use it. I keep it to just checking one simple condition and returning single values:

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

We actually have some nasty code like this in production...not good:

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

The ternary operator is extremely useful for concisely producing comma separated lists. Here is a Java example:

    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());

It produces: "1, 2, 3"

Otherwise, special casing for the last comma becomes annoying.

If you are trying to reduce the amount of lines in your code or are refactoring code, then go for it.

If you care about the next programmer that has to take that extra 0.1 millisecond to understand the expression, then go for it anyway.

I love them, especially in type-safe languages.

I don't see how this:

int count = (condition) ? 1 : 0;

is any harder than this:

int count;


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

I'd argue that ternary operators make everything less complex and more neat than the alternative.

For simple tasks, like assigning a different value depending on a condition, they're great. I wouldn't use them when there are longer expressions depending on the condition though.

If you and your workmates understand what they do and they aren't created in massive groups I think they make the code less complex and easier to read because there is simply less code.

The only time I think ternary operators make code harder to understand is when you have more than three or foyr in one line. Most people don't remember that they are right based precedence and when you have a stack of them it makes reading the code a nightmare.

No, ternary operators do not increase complexity. Unfortunately, some developers are so oriented to an imperative programming style that they reject (or won't learn) anything else. I do not believe that, for example:

int c = a < b ? a : b;

is "more complex" than the equivalent (but more verbose):

int c;
if (a < b) {
c = a;
} else {
c = b;
}

or the even more awkward (which I've seen):

int c = a;
if (!a < b) {
c = b;
}

That said, look carefully at your alternatives on a case-by-case basis. Assuming a propoerly-educated developer, ask which most succinctly expresses the intent of your code and go with that one.

Like so many opinion questions, the answer is inevitably: it depends

For something like:

return x ? "Yes" : "No";

I think that is much more concise (and quicker for me to parse) than:

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

Now if your conditional expression is complex, then the ternary operation is not a good choice. Something like:

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

is not a good candidate for the ternary operator.

As an aside, if you are a C programmer, GCC actually has an extension that allows you to exclude the if-true portion of the ternary, like this:

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

Which will set x to y assuming y is not NULL. Good stuff.

I used to be in the “ternary operators make a line un-readable” camp, but in the last few years I’ve grown to like them when used in moderation. Single line ternary operators can increase readability if everybody on your team understands what’s going on. It’s a concise way of doing something without the overhead of lots of curly braces for the sake of curly braces.

The two cases where I don’t like them: if they go too far beyond the 120 column mark or if they are embedded in other ternary operators. If you can’t quickly, easily and readably express what you’re doing in a ternary operator. Then use the if/else equivalent.

It depends :)

They are useful when dealing with possibly null references (BTW: Java really needs a way to easily compare two possibly null strings).

The problem begins, when you are nesting many ternary operators in one expression.

No (unless they're misused). Where the expression is part of a larger expression, the use of a ternary operator is often much clearer.

I like them. I don't know why, but I feel very cool when I use the ternary expression.

I've seen such beasts like (it was actually much worse since it was isValidDate and checked month and day as well, but I couldn't be bothered trying to remember the whole thing):

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

where, plainly, a series of if-statements would have been better (although this one's still better than the macro version I once saw).

I don't mind it for small things like:

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

or even slightly tricky things like:

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

By the measure of cyclomatic complexity, the use of if statements or the ternary operator are equivalent. So by that measure, the answer is no, the complexity would be exactly the same as before.

By other measures such as readability, maintainability, and DRY (don't repeat yourself), either choice may prove better than the other.

I think it really depends on the context they are used in.

Something like this would be a really confusing, albeit effective, way to use them:

 __CRT_INLINE int __cdecl getchar (void)
{
return (--stdin->_cnt >= 0)
?  (int) (unsigned char) *stdin->_ptr++
: _filbuf (stdin);
}

However, this:

c = a > b ? a : b;

is perfectly reasonable.

I personally think they should be used when they cut down on overly verbose IF statements. The problem is people are either petrified of them, or like them so much they get used almost exclusively instead of IF statements.

If you're using the ternary operator for a simple conditional assignment I think it's fine. I've seen it (ab)used to control program flow without even making an assignment, and I think that should be avoided. Use an if statement in these cases.

As others have pointed out they are nice for short simple conditions. I especially like them for defaults (kind of like the || and or usage in JavaScript and Python), e.g.

int repCount = pRepCountIn ? *pRepCountIn : defaultRepCount;

Another common use is to initialize a reference in C++. Since references have to be declared and initialized in the same statement you can't use an if statement.

SomeType& ref = pInput ? *pInput : somethingElse;

No. They are hard to read. If/Else is much easier to read.

This is my opinion. Your mileage may vary.

As so many answers have said, it depends. I find that if the ternary comparison is not visible in a quick scan down the code, then it should not be used.

As a side issue, I might also note that its very existence is actually a bit of an anomaly due to the fact that in C, comparison testing is a statement. In Icon, the if construct (like most of Icon) is actually an expression. So you can do things like:

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

... which I find much more readable than a ternary comparison operator. :-)

There was a discussion recently about the possibility of adding the ?: operator to Icon, but several people correctly pointed out that there was absolutely no need because of the way if works.

Which means that if you could do that in C (or any of the other languages that have the ternary operator), then you wouldn't, in fact, need the ternary operator at all.

string someSay = bCanReadThis ? "No" : "Yes";

In small doses they can reduce the number of lines and make code more readable; particularly if the outcome is something like setting a char string to "Yes" or "No" based on the result of a calculation.

Example:

char* c = NULL;
if(x) {
c = "true";
}else {
c = "false";
}

compared with:

char* c = x ? "Yes" : "No";

The only bug that can occur in simple tests like that is assigning an incorrect value, but since the conditional is usually simple it's less likely the programmer will get it wrong. Having your program print the wrong output isn't the end of the world, and should be caught in all of code review, bench testing and production testing phases.

I'll counter my own argument with now it's more difficult to use code coverage metrics to assist in knowing how good your test cases are. In the first example you can test for coverage on both the assignment lines; if one is not covered then your tests are not exercising all possible code flows.

In the second example the line will show as being executed regardless of the value of X, so you can't be certain you've tested the alternate path (YMMV depending on the ability of your coverage tools).

This matters more with the increasing complexity of the tests.

My recently formulated rule of thumb for determining whether you should use the ternary operator is:

  • if your code is choosing between two different values, go ahead and use the ternary operator.
  • if your code choosing between two different code paths, stick to an if statement.

And be kind to readers of your code. If you are nesting ternary operators, format the code to make that nesting obvious.

One reason no one seems to mention for using the ternary operator, at least in languages, like D, that support type inference is to allow type inference to work for amazingly complicated template types.

auto myVariable = fun();
// typeof(myVariable) == Foo!(Bar, Baz, Waldo!(Stuff, OtherStuff)).


// Now I want to declare a variable and assign a value depending on some
// conditional to it.
auto myOtherVariable = (someCondition) ? fun() : gun();


// If I didn't use the ternary I'd have to do:
Foo!(Bar, Baz, Waldo!(Stuff, OtherStuff)) myLastVariable;  // Ugly.
if(someCondition) {
myLastVariable = fun();
} else {
myLastVariable = gun():
}

I like the operator in some situations, but I think some people tend to overuse it and that it can make the code harder to read.

I recently stumbled across this line in some open source code I am working to modify.

Where

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

Instead of

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

I wonder if the ternary use adds extra overhead to the LINQ statement in this case.

I agree with the sentiments of many of the posters here. The ternary operator is perfectly valid as long as it is used correctly and does not introduce ambiguity (to be fair, you can say that about any operator/construct).

I use the ternary operator often in embedded code to clarify what my code is doing. Take the following (oversimplified for clarity) code samples:

Snippet 1:

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

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

Here, I am dispatching an input or output request. The process is the same whether the request is a read or a write, only the default I/O size changes. In the first sample, I use the ternary operator to make it clear that the procedure is the same and that the size field gets a different value depending on the I/O direction. In the second example, it is not as immediately clear that the algorithm for the two cases is the same (especially as the code grows much longer than three lines). The second example would be more difficult to keep the common code in sync. Here, the ternary operator does a better job of expressing the largely parallel nature of the code.

The ternary operator has another advantage (albeit one that is normally only an issue with embedded software). Some compilers can only perform certain optimizations if the code is not "nested" past a certain depth (meaning inside a function, you increase the nesting depth by 1 every time you enter an if, loop, or switch statement and decrease it by 1 when you leave it). On occasion, using the ternary operator can minimize the amount of code that needs to be inside a conditional (sometimes to the point where the compiler can optimize away the conditional) and can reduce the nesting depth of your code. In some instances, I was able to re-structure some logic using the ternary operator (as in my example above) and reduce the nested depth of the function enough that the compiler could perform additional optimization steps on it. Admittedly this is a rather narrow use case, but I figured it was worth mentioning anyway.

The ternary operator hands down. They aren't complex if you format properly. Take the leap year example from paxdiablo:

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

This can be written more concise and be made much more readable with this formatting:

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

(Hack of the day)

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

Then you can do if-then-else as expression:

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

I would say that the number of conditions in a logic expression make it harder to read. This is true of an if statement and this is true of a ternary operator. In a perfect world, there should be one summarizable reason for taking a branch as opposed to others. Chances are that it really is more of a "business rule" if your explanation is "only when this cluster of states occur".

However, in the real world, we don't add intermediate steps to fold states into one expressible state simply to obey the ideal case. We have made inferences about multiple states and have to make a decision on how to handle them.

I like ternaries because it's possible to do anything with an if statement.

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

On the other hand:

a += ( object.testSomeCondition() ? 0 : 1 );

makes it clear that the goal is to find a value for a. Of course, in line with that, there probably shouldn't be more than reasonable side effects.

  • I use an if for long or complex conditions after I've decided whether I have the time to rework conditions upstream so that I'm answering an easier question. But when I use an if, I still try to do parallel processing, just under a different condition.

      if (  user.hasRepeatedlyPressedOKWithoutAnswer()
    && me.gettingTowardMyLunchtime( time )
    ) {
    ...
    }
    
  • Also my goal is near-single-stream processing. So I often try not to do an else and an if is simply a step off the common path. When you do a lot of single-stream processing, it's much harder for bugs to hide in your code waiting for that one condition that will jump out and break things.

  • As I said above, if you use a ternary to set one thing, or you have a small number of cases you want to test in order to set it to a value, then I just like the readability of a ternary.

  • With one caveat--> NO COMPLEX true CLAUSES

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

Of course that can be decomposed into:

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

And it looks like a (compressed) truth table.

Remember that there are more important factors for readability. One of them is block length and another is indentation level. Doing simple things in ternaries doesn't create an impetus to further and further levels of indentation.

Making code smaller doesn't always mean it's easier to parse. It differs from language to language.

In PHP for example, whitespace and line-breaks are encouraged since PHP's lexer first breaks the code up in bits starting with line-breaks and then whitespace. So I do not see a performance issue, unless less whitespace is used.

Bad:

($var)?1:0;

Good:

($var) ? 1 : 0;

It doesn't seem like a big issue, but with lexing code in PHP, whitespace is essential. Plus, it also reads a bit better this way.

Use it to:

  • access object (array) properties:

    var status = statuses[error == null ? 'working' : 'stopped'];
    
  • return statements:

    function getFullName(){
    return this.isMale() ? "Mr. " : "Ms. " + this.name;
    }
    
  • initialize variables:

    var formMethod = DEBUG_FLAG == true ? "GET" : "POST";
    
  • validate arguments:

    function(object){
    var prop1 = typeof object.property == 'undefined'
    ? "default prop"
    : object.property;
    //...
    }
    

Code examples are in JavaScript.