Defending the ternary operator

Andrew Koenig ark at research.att.com
Sat Feb 8 11:27:36 EST 2003


Laura> Because I am one.  Because I know so many of them.  Because
Laura> 'lazy work' and 'hurried work' has been the bane of all
Laura> creators since we first started creating anything.

As an experiment, I went back through a 2800-line C program that I
wrote about 20 years ago (!) to see how often I used ?: and in what
context.  I figure that reading my own 20-year-old code is probably a
lot like reading some else's code.


Here are all the places in which I used the ?: operator.


EXAMPLE 1:

        p = <null-terminated string>;
        q = <fixed-length char array>;
        for (i = 0; i < sizeof(<fixed-length char array>); i++) {
            if (*q++ != (*p? *p++: ' ')) {
                   /* Complain that the two strings aren't equal */
            }
        }

As a C programmer, I have no trouble understanding what's going on:
The "if" compares the next character in q with either the next
character in p or a blank, depending on whether we've hit the null
that terminates p.

If I didn't have ?:, and I wanted to rewrite the if statement to have
the same effect, I think I would do something like this:

        p = <null-terminated string>;
        q = <fixed-length char array>;
        for (i = 0; i < sizeof(<fixed-length char array>); i++) {
            char c = ' ';
            if (*p)
                c = *p++;
            if (*q++ != c) {
                    /* Complain that the two strings aren't equal */
            }
        }



EXAMPLE 2:

        mode = cvlong(param, strlen(param), 8) |
            (c == 'c'? S_IFCHR: S_IFBLK);

Before you flame at me for using an absolute constant here, I should
point out that the "8" in the argument to cvlong means "Treat this
as an octal number, please."  Again, without ?: I might have written

        mode = cvlong(param, strlen(param), 8);
        if (c == 'c')
            mode |= S_IFCHR;
        else
            mode |= S_IFBLK;



EXAMPLE 3:

        fprintf(tf, "%c %#o %d %d ",
           mode == S_IFBLK? 'b': 'c',
           buf->st_mode & 07777,
           major(buf->st_rdev),
           minor(buf->st_rdev));

Without ?: I would either have to introduce an extra variable or
write something like this:

        if (mode == S_IFBLK)
            fprintf(tf, "b");
        else
            fprintf(tf, "c");
        fprintf(tf, " %#o %d %d ",
           buf->st_mode & 07777,
           major(buf->st_rdev),
           minor(buf->st_rdev));



EXAMPLE 4:

       fprintf(stderr, "package %s\n",
           strcmp(pack->iname, instr)? pack->ename: instr);

I would never write code like this today, because it uses strcmp as a
condition?  When you do that, the condition is true if the strings are
*un*equal, a fact that is easy to forget.  But let's brush that
aside, and see how to rewrite it without ?:.  I get this:

       if (strcmp(pack->iname, instr))
          fprintf(stderr, "package %s\n", pack->ename);
       else
          fprintf(stderr, "package %s\n", instr);



EXAMPLE 5:

       fprintf(file,
           *p >= '0' && *p <= '7'? "\\%.3o": "\\%o",
           c);

This code is a little sneaky.  It's in the middle of a routine that
writes a printable representation of an arbitrary string.  So what it's
doing is peeking ahead one character.  If that character is an octal digit,
it's writing the present character out as a 3-character octal escape;
otherwise it's using the shortest escape possible.  In other words, this
code would represent ^Gx as \7x but ^G6 as \0076, taking advantage of the
fact that octal escapes are limited to three characters.

Again, let's brush aside questions about what the code is trying to do,
and consider how it does it.  Without ?:, I think it would look like this:

        if (*p >= '0' && *p <= '7')
            fprintf(file, "\\%.3o", c);
        else
            fprintf(file, "\\%o", c);



EXAMPLE 6:

        p = (s == NULL)? str: s;

This example sets a default value to substitute for s in case s is NULL.
The likely rewrite:

        if (s == NULL)
            p = str;
        else
            p = s;



EXAMPLE 7:

Here, hrout and mrout are functions:

        r = (*(line[0] == '.'? mrout: hrout)) (line, out);

So it's choosing which function to call based on the value of line[0].
The rewrite:

        if (line[0] == '.')
            r = mrout(line, out);
        else
            r = hrout(line, out);




So... I used ?: 7 times in 2800 lines, or once every 400 lines or so.


Looking back on these examples, I find that in the first five of them,
the use of ?: makes the code easier to understand, even after 20 years.
For #6, I think it's a wash.  For #7, I think the rewrite is slightly
easier to understand, but not because of the ?: -- rather, the rewrite
is easier because it doesn't use the notion that the name of a function
decays into a pointer to the function.  In Python, I'd prefer the original.




-- 
Andrew Koenig, ark at research.att.com, http://www.research.att.com/info/ark




More information about the Python-list mailing list