4

Background:

Often, we developers must check if a single variable is at least one of many options. For example,

if ( (data == 125) || (data == 500) || (data == 750) )
{
    /* ... do stuff ...*/
}

The suggestion here (albeit written in C#), provides an elegant solution to use a switch statement like so,

switch ( data )
{
    case 125:
    case 500:
    case 750:
        /* ... do stuff ...*/
        break;

    default:
        /* ... do nothing ... */
        break;
}

This works well for "or" conditionals, but is ugly for negated "or" conditionals like the following,

if ( !( (data == 125) || (data == 500) || (data == 750) ) )
{
    /* ... do stuff ...*/
}

which could be written as

switch ( data )
{
    case 125:
    case 500:
    case 750:
        /* ... do nothing ... */
        break;

    default:
        /* ... do stuff ...*/
        break;

}

and seems a bit hackish.

Question:

Is there a more succinct way to check if a single variable is none of many options, like the negated "or" conditional above?

References:

Community
  • 1
  • 1
Eric Schnipke
  • 482
  • 1
  • 6
  • 22
  • 2
    I rather like your last code example. It's very clear what is happening there. – Robert Harvey Jun 29 '16 at 15:53
  • 1
    Is it "hackish" just because the work is done after the `default` label rather than in the fall-throughs? I wouldn't agree. – unwind Jun 29 '16 at 15:53
  • With such questions as to what is "most" or "best", the best code style, best to maintain style, simplest source code, most portable, fastest, least source code, least executable code, least memory are competing factors. Being explicit in term of coding goals helps arrive at the best answer. "Most succinct" sounds like [code golf](http://codegolf.stackexchange.com) which I am sure was not the intent. – chux - Reinstate Monica Jun 29 '16 at 16:17
  • 1
    I like your second example more as well. Given you provide a "do nothing" comment in the "excludes cases" (which you didn't) it's very clear what is going on - Not the least bit of 'hackish' IMHO. – tofro Jun 29 '16 at 17:25
  • Thanks @tofro, I've added your suggestion to improve the question clarity. – Eric Schnipke Jun 29 '16 at 17:35

3 Answers3

8

I think the latter is fine.

You can formalize it better, though:

static bool in_sprawling_set(int data)
{
  switch ( data )
  {
    case 125:
    case 500:
    case 750:
        return true;
  }
  return false;
}

and then where you want to do the work:

if(!in_sprawling_set(data))
{
  /* do the work, not in set */
}

This puts the "in set" logic in a function of its own, makes it mildly self-documenting, and the actual use-place much cleaner since the ! becomes more prominent and the final if is very readable ("if not in sprawling set").

Note: if the number of values is really large, I'd probably go for using a pre-sorted array and a binary search, rather than a huge switch. I realize a sufficiently clever compiler can do that transform by itself, but the readability of a huge switch would be rather low (especially if you like to put only one case per line). There's bsearch() for the searching:

static int cmp_int(const void *ap, const void *bp)
{
  const int a = *(const int *) ap, b = *(const int *) bp;
  return a < b ? -1 : a > b;
}

static bool in_sprawling_set(int data)
{
  static const int values[] = { 125, 500, 750 };
  return bsearch(&data, values, sizeof values / sizeof *values, sizeof *values, cmp_int) != 0;
}

There's quite a lot of boilerplate going on, but you can see how the part that lists the actual values (the only thing that'll grow as more values are added) is more compact.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • Note, that by default GCC is warning about `cases` without `break`. – Eugene Sh. Jun 29 '16 at 15:57
  • 1
    @EugeneSh. Even if it ends with a `return`? Are you sure? – unwind Jun 29 '16 at 16:00
  • Umm.. Let me check. – Eugene Sh. Jun 29 '16 at 16:03
  • Yeah, you are right, it is not. Was referring to a related issue, when you want to fall-through *after* doing some case-specific job, like `case A: doA(); case B: doB(); break;` instead of `case A: doAandB(); break; case B: doB(); break;` – Eugene Sh. Jun 29 '16 at 16:09
  • Thanks for the response @unwind! I think your solution is very elegant and particularly like the way you expanded the idea to anticipate large comparison sets in your note at the end. Thanks, again! – Eric Schnipke Jun 29 '16 at 16:09
  • I like your `bsearch()` approach for a large number of deflection cases. It does, however have the disadvantage over the `switch...case` approach that you cannot put the most probable deflection cases (if you have a probability) towards the beginning for speed. In such a case you would want to simply iterate through the array sorted by probability. – tofro Jun 29 '16 at 18:01
  • @tofro I don't think there's such a guarantee, that labels closer to the top of the `switch` will be reached faster. In practice perhaps, for certain compilers etc. – unwind Jun 30 '16 at 07:31
  • Nope, the standard makes no guarantee on that, right. Experience with various compilers, however, tells me it's most often the case (and actually, I can't see a lot of reasons why a compiler should re-shuffle the order of cases.) Supporting fall-thru obviously *requires* the compiler to keep the order, though. The proposed variation of your solution (iteration through an array sorted by priority), however, can never be changed by a compiler. – tofro Jun 30 '16 at 08:18
1

Instead of negating the condition, you can always use De-morgans laws to simplify the expression

if (data != 125 && data != 500 && data != 750) ...
cup
  • 7,589
  • 4
  • 19
  • 42
-1

Is there a more succinct way to check if a single variable is none of many options?

The switch() statement is certainly a fine solution.

As an alternative, if the product does not over flow, code could use a single branch test:

unsigned data = foo();
if ( (data - 125) * (data - 500) * (data - 750) ) {
  /* ... do stuff as long as data is not 125, 500  or 750 ...*/
}

If it more clear - not really, Is it faster than switch()? it has potential.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Very interesting alternative @chux. This made me think more deeply about optimizing conditional speed and the implications for my project. Thanks for the response! – Eric Schnipke Jun 29 '16 at 16:18
  • What would be an advantage over the *compare+or* approach? I see none. Same number of terms and operators. In addition, the multiplication is not (necessarily) short-circuiting. – Eugene Sh. Jun 29 '16 at 16:22
  • @Eric Schnipke When speed is important, then one should also consider the set of typical values of `data`. Various approaches can be optimized to that set. Are `125,500,750` rare or common? I re-call [bison](https://en.wikipedia.org/wiki/GNU_bison) as tool to make fast parsers – chux - Reinstate Monica Jun 29 '16 at 16:23
  • @Eugene Sh. By " compare+or" I suppose you mean `(data == 125) || (data == 500) || (data == 750)` - that code can well cause multiple branches - this can be slow on pipelined processors. Same with `(data == 125) | (data == 500) | (data == 750)`. On pipelined processors, lack of short-circuiting is often faster. As in previous comment, speed comparison are affected by the set distribution of `data. In the end - sometime a smart compiler will make optimal code given various source code. – chux - Reinstate Monica Jun 29 '16 at 16:31
  • @Eugene and Eric: For speed on advanced processors, consider http://stackoverflow.com/a/11227902/2410359 – chux - Reinstate Monica Jun 29 '16 at 16:41
  • @Michael Smith Curious you find `if ( (data - 125) * (data - 500) * (data - 750) )` quite a bit more overhead than `switch ( data ) { case 125: case 500: case 750: break; default: /* ... do stuff ...*/ break; }` OP' "Most succinct" could be taken as a request for a high performance solution versus a code golf. Consider a non-branching solution adds value as commented by [OP](http://stackoverflow.com/questions/38104445/c-most-succinct-way-to-check-if-a-variable-is-none-of-many-options/38104852?noredirect=1#comment63643795_38104852) – chux - Reinstate Monica Jun 29 '16 at 17:11