Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C - Most succinct way to check if a variable is *none* of many options?

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:

  • C++ Most efficient way to compare a variable to multiple values?
  • C# Comparing a variable to multiple values
like image 357
Eric Schnipke Avatar asked Feb 06 '23 16:02

Eric Schnipke


2 Answers

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.

like image 186
unwind Avatar answered Feb 16 '23 04:02

unwind


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

if (data != 125 && data != 500 && data != 750) ...
like image 28
cup Avatar answered Feb 16 '23 04:02

cup