Replacing an Else-if Sequence With a Ternary Operator

One of the comments left on the Reddit thread of How to make if statements more understandable by /u/loup-vaillant, showed a suggestion to represent an else-if logic a different way, by using the ternary operator ( ?: ) in a certain way. I find that suggestion interesting and I’d like to share it with you today.

And while you’re here, you may also want to check out other articles about if statements in general:

Compacting an else-if with the ternary operator

Consider the following code. It displays a 20×20 square representing a geometrical layout of characters, following these rules in this order of priority:

if x + y >= 30 (lower-right end), display periods

if 25 <= x + y < 30 (next slice up), display slashes

if 20 <= x + y < 25 (next next slice up), display o’s

if x – 3y > 0 (triangle slice starting at the origin), display pipes

if x – y > 0 (other triangle slice starting at the origin), diplay backslashes

fill the rest with underscores.

for (int y = 0; y < 20; ++y) { for (int x = 0; x < 20; ++x) { char displayedChar = 0; if (x + y >= 30) { displayedChar = '.'; } else if (x + y >= 25) { displayedChar = '/'; } else if (x + y >= 20) { displayedChar = 'o'; } else if (x - 3*y > 0) { displayedChar = '|'; } else if (x - y > 0) { displayedChar = '\\'; } else { displayedChar = '_'; } std::cout << displayedChar << ' '; } std::cout << '

'; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 for ( int y = 0 ; y < 20 ; ++ y ) { for ( int x = 0 ; x < 20 ; ++ x ) { char displayedChar = 0 ; if ( x + y >= 30 ) { displayedChar = '.' ; } else if ( x + y >= 25 ) { displayedChar = '/' ; } else if ( x + y >= 20 ) { displayedChar = 'o' ; } else if ( x - 3 * y > 0 ) { displayedChar = '|' ; } else if ( x - y > 0 ) { displayedChar = '\\' ; } else { displayedChar = '_' ; } std :: cout << displayedChar << ' ' ; } std :: cout << '

' ; }

for (int y = 0; y < 20; ++y) { for (int x = 0; x < 20; ++x) { char displayedChar = (x + y >= 30) ? '.' : (x + y >= 25) ? '/' : (x + y >= 20) ? 'o' : (x - 3*y > 0) ? '|' : (x - y > 0) ? '\\' : '_'; std::cout << displayedChar << ' '; } std::cout << '

'; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 for ( int y = 0 ; y < 20 ; ++ y ) { for ( int x = 0 ; x < 20 ; ++ x ) { char displayedChar = ( x + y >= 30 ) ? '.' : ( x + y >= 25 ) ? '/' : ( x + y >= 20 ) ? 'o' : ( x - 3 * y > 0 ) ? '|' : ( x - y > 0 ) ? '\\' : '_' ; std :: cout << displayedChar << ' ' ; } std :: cout << '

' ; }

_ | | | | | | | | | | | | | | | | | | | _ _ \ \ | | | | | | | | | | | | | | | o _ _ _ \ \ \ \ | | | | | | | | | | | o o _ _ _ _ \ \ \ \ \ \ | | | | | | | o o o _ _ _ _ _ \ \ \ \ \ \ \ \ | | | o o o o _ _ _ _ _ _ \ \ \ \ \ \ \ \ \ o o o o o _ _ _ _ _ _ _ \ \ \ \ \ \ \ o o o o o / _ _ _ _ _ _ _ _ \ \ \ \ \ o o o o o / / _ _ _ _ _ _ _ _ _ \ \ \ o o o o o / / / _ _ _ _ _ _ _ _ _ _ \ o o o o o / / / / _ _ _ _ _ _ _ _ _ _ o o o o o / / / / / _ _ _ _ _ _ _ _ _ o o o o o / / / / / . _ _ _ _ _ _ _ _ o o o o o / / / / / . . _ _ _ _ _ _ _ o o o o o / / / / / . . . _ _ _ _ _ _ o o o o o / / / / / . . . . _ _ _ _ _ o o o o o / / / / / . . . . . _ _ _ _ o o o o o / / / / / . . . . . . _ _ _ o o o o o / / / / / . . . . . . . _ _ o o o o o / / / / / . . . . . . . . _ o o o o o / / / / / . . . . . . . . . 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 _ | | | | | | | | | | | | | | | | | | | _ _ \ \ | | | | | | | | | | | | | | | o _ _ _ \ \ \ \ | | | | | | | | | | | o o _ _ _ _ \ \ \ \ \ \ | | | | | | | o o o _ _ _ _ _ \ \ \ \ \ \ \ \ | | | o o o o _ _ _ _ _ _ \ \ \ \ \ \ \ \ \ o o o o o _ _ _ _ _ _ _ \ \ \ \ \ \ \ o o o o o / _ _ _ _ _ _ _ _ \ \ \ \ \ o o o o o / / _ _ _ _ _ _ _ _ _ \ \ \ o o o o o / / / _ _ _ _ _ _ _ _ _ _ \ o o o o o / / / / _ _ _ _ _ _ _ _ _ _ o o o o o / / / / / _ _ _ _ _ _ _ _ _ o o o o o / / / / / . _ _ _ _ _ _ _ _ o o o o o / / / / / . . _ _ _ _ _ _ _ o o o o o / / / / / . . . _ _ _ _ _ _ o o o o o / / / / / . . . . _ _ _ _ _ o o o o o / / / / / . . . . . _ _ _ _ o o o o o / / / / / . . . . . . _ _ _ o o o o o / / / / / . . . . . . . _ _ o o o o o / / / / / . . . . . . . . _ o o o o o / / / / / . . . . . . . . .

The suggestion consists in replacing this stretching else-if sequence by repeated calls to the ternary operator:And both pieces of code output this breathtaking result:(The original comment was not on this particular example, instead it took the case of the corresponding article).

The code is not that hard to understand so I don’t think it deserves more explanation. Instead you can let your mind wander into the unexplicably captivating turns of our virtual painting. Just for a minute.

Anyway, the point, at least as I understand it, is that the ternary operator, with the right indentation, makes the code looks like the specification written above in English. And it’s a good thing, because if statements should do their best to look like their specifications.

Why don’t we see this pattern more often then?

One limitation to this technique compared to the else-if sequence is that there can’t be more that one statement for each conditional. If you need to do two things, or instantiate an intermediary variable, the whole thing must replaced by a classical else-if sequence. Unless you take the statements out into a separate function.

EDIT: as pointed out by reader jft, there is a way to fit several instructions, by using the comma operator.

Another thing I noticed is that it’s clearer when it has the indentiation right: all question marks of the ternary operator had better be aligned to make the thing pretty overall. So you need to make sure the indentation of the whole structure stays correct each time you modify it.

And maybe another reason we don’t see this often is because… we don’t see it often. For this reason some could consider it “cute” code, as the expression goes. Not cute in the good way, but rather cute like that:

(it’s kinda cute, isn’t it?)

These are the reasons I could see to explain why this is a rarely seen practice.

Things to know about the ternary operator

Anyway, if we’re considering using this in our code, it’s a good time for talking about the specificities of the ternary operator. Beyond the obvious thing of what the ternary operator is actually doing ( b ? x : y evaluates to x if b is true , and y if b is false ), I can see three of its aspects that are worth mentioning.

First, watch out for operator precedence: the precedence of the ternary operator is pretty low, lower than + , - , && and other common operators. This means that a piece of code that doesn’t have the right parentheses may lead to surprising results, as demonstrated in one of the 42 tips on C++ on the blog of the static analyzer PVS-Studio.

Second, the two possible outcomes of the ternary operator don’t have to be of the same exact type. If one is convertible to the other then it’s enough to have a common type, which will be the result of the ternary expression.

Finally, the evaluated results can be of type void and/or have side effects. So the ternary operator doesn’t have to return anything, although it often does in practice. For instance, the following code:

void print(std::string const& s) { std::cout << s << '

'; } isTernaryCool ? print("model") : print("toad"); 1 2 3 4 5 6 void print ( std :: string const & s ) { std :: cout << s << '

' ; } isTernaryCool ? print ( "model" ) : print ( "toad" ) ;

displays model if isTernaryCool evaluates to true and displays toad otherwise.

It looks like Lisp, right?

One of my goals for this summer was to become familiar with Lisp. I must say it is a fascinating language, and the book the Land of Lisp that I’m using is lots of fun. If you’re not sure yet how to improve your skills this summer, I’ve got 7 ways to get better at C++ this summer for you.

Anyway, this usage of the ternary operator reminds me of the cond function in Lisp:

(cond ((>= (+ x y) 30) #\.) ((>= (+ x y) 25) #\/) ((>= (+ x y) 20) #\o) ((> (- x (* 3 y)) 0) #\|) ((> (- x y) 0) #\\) (t #\_)) 1 2 3 4 5 6 ( cond ( ( >= ( + x y ) 30 ) # \ . ) ( ( >= ( + x y ) 25 ) # \ / ) ( ( >= ( + x y ) 20 ) # \ o ) ( ( > ( - x ( * 3 y ) ) 0 ) # \ | ) ( ( > ( - x y ) 0 ) # \ \ ) ( t # \ _ ) )

The cond function takes a collection of scenarios that consists each of a condition associated to an expression. The cond function successivelly tries out every condition until it finds one that evaluates to true (or should I rather say, to t ), and evaluates the associated expression. It looks in principle like an else-if sequence to me, but much more concise. And our usage of the ternary operator in C++ really looks like it, even in terms of layout of the code.

The good sign for it is that Lispers seem to very much appreciate using the cond function.

What’s your take on this? I’d love to know it. Do you find this usage of the ternary operator too “cute” to be reasonable for production code? And a big thank you to /u/loup-vaillant for reacting to the previous post. If you have other remarks or any kind of feedback, voice it!

Related articles:

Share this post! Don't want to miss out ?