HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Composing terminal control sequences

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
sequencescomposingterminalcontrol

Problem

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.

/// 
/// Control sequence character
/// 
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

Solution

I prefer the switch statement. Here are my reasons:

-
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.

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

-
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.

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

Code Snippets

x = y & mask ? 8 : 4;
a = 5 + (b > c) ? b : c;
x = (y & mask) ? 8 : 4;
a = 5 + ((b > c) ? b : c);
// (pseudocode)
offset = jumpTable[index];
goto offset;

Context

StackExchange Code Review Q#86011, answer score: 5

Revisions (0)

No revisions yet.