3
\$\begingroup\$

I've seen everybody claims the nested ternary to be an evil even when an operation couldn't be written with switch-case; and here's even more, my code could be easily written with switch-case statement.

/// <summary>
/// Control sequence character
/// </summary>
private readonly string CSI = "\u001b";

public string ComposeTermSeqs1(TermActions action)
{
    switch(action) {
        case TermActions.Right:
            return CSI + "[C";
        case TermActions.Up:
            return CSI + "[A";
        case TermActions.Left:
            return CSI + "[D";
        case TermActions.Down:
            return CSI + "[B";
        case TermActions.ForwardWord: //readline lib
            return CSI + "f";
        case TermActions.BackWord: //readline lib
            return CSI + "b";
        case TermActions.BackSearch:
            return "\u0012";
        case TermActions.Interrupt:
            return "\u0003";
        default: //TermActions.SuspendApp:
            return "\u001a";
    }
}

But when I am looking at the ternary version of the function…

public string ComposeTermSeqs2(TermActions action)
{
    return (action == TermActions.Right)      ? CSI + "[C"
        : (action == TermActions.Up)          ? CSI + "[A"
        : (action == TermActions.Left)        ? CSI + "[D"
        : (action == TermActions.Down)        ? CSI + "[B"
        : (action == TermActions.ForwardWord) ? CSI + "f" //readline lib
        : (action == TermActions.BackWord)    ? CSI + "b" //readline lib
        : (action == TermActions.BackSearch)  ? "\u0012"
        : (action == TermActions.Interrupt)   ? "\u0003"
        : "\u001a";
}

I don't know, the switch-case is α) longer β) is harder to maintain because you ought to write every time one case in the «default», and γ) the ternary version is pure, i.e. here's no need to tackle with debugging of this function when something gone wrong, only with the result. You might say that the first version is pure too, but actually someone could easily add a side code to a case.

I am hesitating… Which one should I choose?

\$\endgroup\$
  • \$\begingroup\$ Yeah, I noticed that I ought to add a default condition for the case when someone added a field to an enum, and forgot to implement. Here's no way to check it for compilation time. Anyway, a bit late to change the code, so be it. \$\endgroup\$ – Hi-Angel Apr 6 '15 at 10:24
  • 1
    \$\begingroup\$ Switch statement is just as short if you move each return statement up to the same line as the case statement. I don't understand your other arguments. \$\endgroup\$ – JS1 Apr 6 '15 at 10:39
  • \$\begingroup\$ @JS1 hm, it actually makes a sense. The β is off, I wrote why in the first comment. The γ is about the fact that it is obvious in the ternary version that the code dosn't do side effects. But with your advice to move the return to the same line as a case, it is indeed much easier to see, what's going on. But still I can't see, why not just use the ternary version? What is bad about it here? \$\endgroup\$ – Hi-Angel Apr 6 '15 at 10:56
  • 1
    \$\begingroup\$ I find it a lot easier to make a syntax error with ternary operator. Also, to me it's harder to read. Lastly, the switch statement might be faster depending on your compiler. \$\endgroup\$ – JS1 Apr 6 '15 at 11:01
  • \$\begingroup\$ @JS1 the both arguments, about the easier syntax error, and harder to read is actually tied. When something unusual, it is of course easy to make a syntax error; the compiler would say about it anyway ☺ The argument about the speed looks to me non-sense on the lower level it just a comparison of a value, and executing a branch of code. It's like to say that nested if-lese-if… faster than switch-case. \$\endgroup\$ – Hi-Angel Apr 6 '15 at 11:11
5
\$\begingroup\$

I prefer the switch statement. Here are my reasons:

  1. You can move each return statement onto the same line as the case statement to make the switch statement just as short as the ternary version.

  2. The ternary version is harder to read (at least for me) due to the more ? : == and () involved. This is not a big deal though.

  3. I find it easier to make subtle syntax errors (not compile time errors) with the ternary operator. For example:

    x = y & mask ? 8 : 4;
    a = 5 + (b > c) ? b : c;
    

    Of course you can avoid this by using the correct parentheses:

    x = (y & mask) ? 8 : 4;
    a = 5 + ((b > c) ? b : c);
    

    It's just that sometimes these kind of expressions appear across multiple lines and it gets hard to visualize which part is which.

  4. A compiler might be able to do a better job optimizing a switch statement than the ternary if-else ladder. For example, if your switch cases are in consecutive order (such as 0..N), then the compiler can use a jump table to simplify the switch into:

    // (pseudocode)
    offset = jumpTable[index];
    goto offset;  
    
\$\endgroup\$
4
\$\begingroup\$

The switch statement is definitely to be preferred in this case. Not only do I consider it more readable, but it is also handled differently by the compiler. As said by @JS1 in the comments, the compiler is able to transform a switch statement into a \$O(1)\$ operation using a jump table. A nested ternary is essentially a line of if-else-if-else-if-else... which is a \$O(n)\$ operation.

If you really think the switch takes too much space to write, remove some newlines:

public string ComposeTermSeqs1(TermActions action)
{
    switch(action) {
        case TermActions.Right: return CSI + "[C";
        case TermActions.Up:    return CSI + "[A";
        case TermActions.Left:  return CSI + "[D";
        case TermActions.Down:  return CSI + "[B";
        case TermActions.ForwardWord: return CSI + "f";
        case TermActions.BackWord:  return CSI + "b";
        case TermActions.BackSearch: return "\u0012";
        case TermActions.Interrupt:  return "\u0003";
        default: return "\u001a";
    }
}

A or B? How about C?

A third alternative, that you at least might want to think about, is using a Dictionary. Add all the TermActions as keys and the return values as values in the dictionary. This will be one line for creating the dictionary, one line for each item to add to it, and one line to get the value from it.

\$\endgroup\$
  • \$\begingroup\$ Thank you very much for the answer, you pointed out an important things. However, I'll wait a bit for the @JS1's answer, because almost everything mentioned they wrote in the discussion and comments, so it would be fair if I give the checkmark them. \$\endgroup\$ – Hi-Angel Apr 6 '15 at 12:04
  • 1
    \$\begingroup\$ @Hi-Angel I agree \$\endgroup\$ – Simon Forsberg Apr 6 '15 at 12:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.