简化/优化一段查看条件组合的代码的最佳方法是什么?

use*_*336 15 algorithm optimization

我有一段代码,我想优化它的可读性和性能以及酷感.现在我有这个丑陋的东西:

if      ( cond1 &&  cond2 &&  cond3 && !cond4)
{
     // do something 
}
else if ( cond1 &&  cond2 && !cond3 &&  cond4)
{
    // do something 
}
else if ( cond1 && !cond2 &&  cond3 &&  cond4)
{
   // do something 
}
else if (!cond1 &&  cond2 &&  cond3 &&  cond4)
{
    // do something 
}
else
{
    // do something 
}
Run Code Online (Sandbox Code Playgroud)

其中cond1,cond2,cond3cond4是已被上面的代码块之前初始化布尔值.我想让它更快,更简单,更酷.

我正在考虑这样做:

int val = (cond1 ? 0 : 1) + 2 * (cond2 ? 0 : 1) + 4 * (cond3 ? 0 : 1) + 8 * (cond4 ? 0 : 1);
if (val == 8)
{
    // do something
}
else if (val == 4)
{
    // do something 
}
else if (val == 2)
{
    // do something
}
else if (val == 1)
{
    // do something
}
else
{
    // do something 
}
Run Code Online (Sandbox Code Playgroud)

这有用还是有缺陷?有没有更好的办法?在查看多个条件的不同组合时,获得所需结果的典型方法是什么?

Apr*_*ori 25

您希望将这些值转换为位标志.也就是说,对于每个条件,您都希望在整数类型中设置或不设置.然后,您的案例中的每个4位值代表您的上述ANDed条件之一.之后,您可以使用switch语句.它可以说更具可读性,编译器通常可以将其优化为跳转表.也就是说,它只是将您的程序计数器偏移到查找表中的某个值或某种类型的值,并且您不再需要检查每个值的组合.通过这种方式,对ANDed情况的检查变为恒定时间而不是线性,即如果你添加了4个标志,现在有256个组合而不是16个,那么这种方法在大方式上同样快.或者,如果您不信任编译器使switch语句成为跳转表,则可以使用该flags值作为函数指针数组的索引来自行完成.值得注意的是ORed枚举案例值在编译时被折叠或预先计算.

  enum {
    C1 = 0x1,
    C2 = 0x2,
    C3 = 0x4,
    C4 = 0x8
  };

  unsigned flags = 0;
  flags |= cond1 ? C1 : 0x0;
  flags |= cond2 ? C2 : 0x0;
  flags |= cond3 ? C3 : 0x0;
  flags |= cond4 ? C4 : 0x0;

  switch (flags) {
    case 0: // !cond1 && !cond2 && !cond3 && !cond4
      // do something
      break;
    case C1: //  cond1 && !cond2 && !cond3 && !cond4
      // do something
      break;
    case C2: // !cond1 &&  cond2 && !cond3 && !cond4
      // do something
      break;
    case C1 | C2: //  cond1 &&  cond2 && !cond3 && !cond4
      // do something
      break;
    case C3: // !cond1 && !cond2 &&  cond3 && !cond4
      // do something
      break;
    case C1 | C3: //  cond1 && !cond2 &&  cond3 && !cond4
      // do something
      break;
    case C2 | C3: // !cond1 &&  cond2 &&  cond3 && !cond4
      // do something
      break;
    case C1 | C2 | C3: //  cond1 &&  cond2 &&  cond3 && !cond4
      // do something
      break;
    case C4: // !cond1 && !cond2 && !cond3 &&  cond4
      // do something
      break;
    case C1 | C4: //  cond1 && !cond2 && !cond3 &&  cond4
      // do something
      break;
    case C2 | C4: // !cond1 &&  cond2 && !cond3 &&  cond4
      // do something
      break;
    case C1 | C2 | C4: //  cond1 &&  cond2 && !cond3 &&  cond4
      // do something
      break;
    case C3 | C4: // !cond1 && !cond2 &&  cond3 &&  cond4
      // do something
      break;
    case C1 | C3 | C4: //  cond1 && !cond2 &&  cond3 &&  cond4
      // do something
      break;
    case C2 | C3 | C4: // !cond1 &&  cond2 &&  cond3 &&  cond4
      // do something
      break;
    case C1 | C2 | C3 | C4: //  cond1 &&  cond2 &&  cond3 &&  cond4
      ; // do something
  };
Run Code Online (Sandbox Code Playgroud)

此外,这涵盖了所有组合.如果您只是需要一些子集随时删除一些案例.编译器非常擅长优化switch语句.它可能比你可以自己动手的任何聪明的特殊情况算术技巧更快.

  enum {
    C1 = 0x1,
    C2 = 0x2,
    C3 = 0x4,
    C4 = 0x8
  };

  unsigned flags = 0;
  flags |= cond1 ? C1 : 0x0;
  flags |= cond2 ? C2 : 0x0;
  flags |= cond3 ? C3 : 0x0;
  flags |= cond4 ? C4 : 0x0;

  switch (flags) {
    case C1 | C2 | C3: //  cond1 &&  cond2 &&  cond3 && !cond4
      // do something
      break;
    case C1 | C2 | C4: //  cond1 &&  cond2 && !cond3 &&  cond4
      // do something
      break;
    case C1 | C3 | C4: //  cond1 && !cond2 &&  cond3 &&  cond4
      // do something
      break;
    case C2 | C3 | C4: // !cond1 &&  cond2 &&  cond3 &&  cond4
      // do something
      break;
    default:
      // do something
      ;
  };
Run Code Online (Sandbox Code Playgroud)


Sne*_*tel 16

好吧,最愉快的写作方式可能就是这样

if(cond1 + cond2 + cond3 + cond4 == 3)
{
    if(!cond1)
    {
        // do something
    }
    else if(!cond2)
    {
        // do something
    }
    else if(!cond3)
    {
        // do something
    }
    else // !cond4
    {
        // do something
    }
}
else
{
    // do something
}
Run Code Online (Sandbox Code Playgroud)

不过,我对那些不在数组中的值很谨慎.

  • 据我所知,那个(从布尔到整数的隐式转换)只能在C或C++中起作用 - 这个问题没有表明语言,但它很可能是Java或C#(特别是考虑到`(cond1?0: 1)`). (2认同)
  • @ user3537336从性能的角度来看,你首先有四个独立的布尔值这一事实引起了更大的关注.从风格的角度来看,避免重新测试条件是一个相当好的经验法则,但应该不时地忽略. (2认同)

ami*_*mit 5

根据我的经验 - 有一系列长if/else语句表示类似但可区分的行为.

我通常尝试引入一个接口实现捕获此行为的,并调用将产生所需结果的方法.

这使得代码更具可读性 - 无需重复复杂的流程(可能会在以后嵌套).每个类负责抽象方法的实现,并且操作的对象将具有静态类型的接口,以及其动态类型 - 最匹配的实现类.