【问题标题】:C - Most succinct way to check if a variable is *none* of many options?C - 检查一个变量是否是许多选项中的 *none* 的最简洁方法?
【发布时间】:2016-06-29 15:47:27
【问题描述】:

背景:

通常,我们开发人员必须检查单个变量是否至少是多个选项之一。例如,

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

建议 here(尽管是用 C# 编写的)提供了一个优雅的解决方案,可以像这样使用 switch 语句,

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

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

这适用于“或”条件,但对于否定的“或”条件则很难看,如下所示,

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

可以写成

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

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

}

而且看起来有点老套。

问题:

是否有更简洁的方法来检查单个变量是否没有多个选项,例如上面的否定“或”条件?

参考资料:

【问题讨论】:

  • 我更喜欢你的最后一个代码示例。那里发生了什么非常清楚。
  • 是否只是因为工作是在default 标签之后完成的,而不是在失败中完成的?我不会同意的。
  • 关于什么是“最多”或“最好”的问题,最好的代码风格,最好的维护风格,最简单的源代码,最便携,最快,最少的源代码,最少的可执行代码,最少的内存是竞争因素。明确编码目标有助于得出最佳答案。 “最简洁”听起来像 code golf,我敢肯定这不是本意。
  • 我也更喜欢你的第二个例子。鉴于您在“排除案例”(您没有)中提供了“什么都不做”评论,所以很清楚发生了什么 - 不是一点点“hackish”恕我直言。
  • 感谢@tofro,我已添加您的建议以提高问题的清晰度。

标签: c if-statement comparison


【解决方案1】:

我认为后者很好。

不过,您可以将其形式化得更好:

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

然后你想在哪里工作:

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

这将“集合”逻辑置于其自己的函数中,使其具有适度的自我记录性,并且由于! 变得更加突出并且最终的if 非常易读,因此实际使用位置更加清晰(“如果不在庞大的集合中”)。

注意:如果值的数量真的很大,我可能会使用预排序数组和二进制搜索,而不是巨大的switch。我意识到一个足够聪明的编译器可以自己完成这种转换,但是一个巨大的switch 的可读性会相当低(特别是如果你想每行只放一个case)。搜索有bsearch()

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

有很多样板,但您可以看到列出实际值的部分(唯一会随着更多值的添加而增长的部分)更加紧凑。

【讨论】:

  • 请注意,默认情况下,GCC 会在没有 break 的情况下警告 cases
  • @EugeneSh。即使它以return 结尾?你确定吗?
  • 嗯..让我检查一下。
  • 是的,你是对的,它不是。指的是一个相关的问题,当你想做一些特定于案例的工作之后失败,比如case A: doA(); case B: doB(); break;而不是case A: doAandB(); break; case B: doB(); break;
  • 感谢@unwind 的回复!我认为您的解决方案非常优雅,特别是您扩展想法以在最后的笔记中预测大型比较集的方式。再次感谢!
【解决方案2】:

你总是可以使用德摩根定律来简化表达式,而不是否定条件

if (data != 125 && data != 500 && data != 750) ...

【讨论】:

    【解决方案3】:

    有没有更简洁的方法来检查单个变量是否不是许多选项中的一个?

    switch() 语句当然是一个很好的解决方案。

    作为替代方案,如果产品没有溢出,代码可以使用单个分支测试:

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

    如果它更清楚 - 不是真的,它比switch() 快吗? 它有潜力

    【讨论】:

    • 非常有趣的替代品@chux。这让我更深入地思考优化条件速度以及对我的项目的影响。感谢您的回复!
    • compare+or 方法相比有什么优势?我没有看到。相同数量的术语和运算符。此外,乘法不是(必然)短路。
    • @Eric Sc​​hnipke 当速度很重要时,还应该考虑data 的一组典型值。可以针对该集合优化各种方法。 125,500,750 是罕见的还是常见的?我重新调用 bison 作为制作快速解析器的工具
    • @Eugene Sh.通过“比较+或”我想你的意思是(data == 125) || (data == 500) || (data == 750) - 该代码很可能导致多个分支 - 这在流水线处理器上可能会很慢。与(data == 125) | (data == 500) | (data == 750) 相同。在流水线处理器上,缺乏短路通常更快。和之前的评论一样,速度比较受数据集分布的影响。最后 - 有时智能编译器会根据各种源代码生成最佳代码。
    • @Eugene 和 Eric:对于高级处理器的速度,请考虑 stackoverflow.com/a/11227902/2410359
    猜你喜欢
    • 2018-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-23
    • 1970-01-01
    • 1970-01-01
    • 2010-09-06
    • 1970-01-01
    相关资源
    最近更新 更多