嵌套冗余'if'条件

HAL*_*HAL 31 c c++ if-statement

是否有更好(或更清洁)的方法来编写以下代码?

if(conditionX)
{
    if(condition1)
    {
        // code X1
    }
    else if(condition2)
    {
        // code X2
    }
}
else if(conditionY)
{
    if(condition1)
    {
        // code Y1
    }
    else if(condition2)
    {
        // code Y2
    }
}
Run Code Online (Sandbox Code Playgroud)

我还有一些条件,但我想你明白了.

das*_*ght 34

这个问题有四种方法,其中没有一种是通用的:

  1. 保持原样 - 这里没有太多的代码重复.如果计算condition1并且condition2很棘手,请预先计算它们并将它们存储在bool变量中
  2. 制作conditionXconditionY产生结果,让您统一condition1condition2 -这并不总是可能的,但在某些情况下,你可以,比方说,使用函数指针或拉姆达准备一个统一的两个分支所采取的活动的变量.
  3. 将处理逻辑放入具有虚函数的子类中以消除条件逻辑 - 只有当您的初始设计错过了子类化的机会时才有可能.本质上,这种方法将决定推入conditionX/ conditionY创建一个创建子类的地方,然后通过调用接口中虚拟函数的正确覆盖来"重用"该决定.
  4. 创建一个表示所有三个条件的数字组合,并转换为switch - 此技巧统一条件,减少嵌套.

以下是最后一种方法的示例:

int caseNumber = ((conditionX?1:0) << 3)
               | ((conditionY?1:0) << 2)
               | ((condition2?1:0) << 1)
               | ((condition1?1:0) << 0);
switch (caseNumber) {
    case 0x09:
    case 0x0D:
    case 0x0F: // code X1
        break;
    case 0x0A:
    case 0x0E: // code X2
        break;
    case 0x05:
    case 0x07: // code Y1
        break;
    case 0x06: // code Y2
        break;
}
Run Code Online (Sandbox Code Playgroud)

  • 除非'caseNumber'*表示*某事,否则第三种方法显然比原始方法更差.IMVHO,至少. (14认同)
  • @vonbrand最后一种方法适用于需要涵盖所有或几乎所有组合的情况.只有四个条件的`if`s是好的链子,但是一旦你到达五或六,一个平面结构,无数的数字和大量的评论胜过多路嵌套,以便于阅读. (3认同)
  • @dasblinkenlight:您不处理案例0x7(代码-Y1),案例0xB(代码-X1),案例0xD(代码-X1),案例0xE(代码-X2)和案例0xF(代码-X1).所有这一切,都是因为`conditionX == true`并不一定意味着`conditionY == false`,反之亦然.同样适用于`condition1`和`condition2`. (3认同)
  • 我同意切换可能比深度嵌套,重复条件更好,但我也同意你想要有意义的案例标签.请参阅barak manos的答案,以便更好地表示案例,以便了解它们对应的条件 (2认同)

pax*_*blo 25

如果您在查看源代码时关注的是干净的代码,我的建议是将块分隔到它们自己的部分,例如:

if      (conditionX) processConditionX();
else if (conditionY) processConditionY();
Run Code Online (Sandbox Code Playgroud)

等等.

然后,在子功能中,放置"肉":

void processConditionX (void) {
    if(condition1) {
        // code X1
    } else if(condition2) {
        // code X2
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以修改它以传入并根据需要返回参数,我会使条件和函数名称更具描述性,但我认为它们只是示例.


bar*_*nos 14

您可以改为实现状态机:

#define COMBINATION(a,b,c,d) (((a)<<3)|((b)<<2)|((c)<<1)|((d)<<0))

switch (COMBINATION(conditionX,conditionY,condition1,condition2))
{
    case COMBINATION(0,0,0,0):           break;
    case COMBINATION(0,0,0,1):           break;
    case COMBINATION(0,0,1,0):           break;
    case COMBINATION(0,0,1,1):           break;
    case COMBINATION(0,1,0,0):           break;
    case COMBINATION(0,1,0,1): CodeY2(); break;
    case COMBINATION(0,1,1,0): CodeY1(); break;
    case COMBINATION(0,1,1,1): CodeY1(); break;
    case COMBINATION(1,0,0,0):           break;
    case COMBINATION(1,0,0,1): CodeX2(); break;
    case COMBINATION(1,0,1,0): CodeX1(); break;
    case COMBINATION(1,0,1,1): CodeX1(); break;
    case COMBINATION(1,1,0,0):           break;
    case COMBINATION(1,1,0,1): CodeX2(); break;
    case COMBINATION(1,1,1,0): CodeX1(); break;
    case COMBINATION(1,1,1,1): CodeX1(); break;
}
Run Code Online (Sandbox Code Playgroud)

这仅包括一个分支操作,因此它可能更高效(即使它还包括额外的运行时计算(switch在线)).

至于更清洁,我想这是一个透视问题,但上面的模板还为您提供了一种检测代码中所有未处理分支的便捷方法.

请注意,如果任何条件变量的值不是1或0,那么您应该:

#define COMBINATION(a,b,c,d) (((a)?8:0)|((b)?4:0)|((c)?2:0)|((d)?1:0))
Run Code Online (Sandbox Code Playgroud)

更新(在以下评论之一中归因于@Jonathan Wakely):

如果你正在使用C++ 11,那么你可以COMBINATION用一个constexpr函数替换宏:

constexpr int COMBINATION(bool a,bool b,bool c,bool d)
{
    return ((int)a<<3) | ((int)b<<2) | ((int)c<<1) | ((int)d<<0);
}
Run Code Online (Sandbox Code Playgroud)

  • 是.这就是`constexpr`功能的用途.宏吮吸.不幸的是,在C语言中你别无选择,但在C++ 11中,constexpr函数由于多种原因而更受欢迎(更清晰,类型安全,使用C++语法而不是容易出错的预处理器编写).例如,如果`conditionX`是一个值为15的`int`,那么你的宏做错了.`constexpr condX(bool b){return int(b)<< 3; 没有那个问题,首先将值转换为`true`,然后在移位后才能生成正确的值 (6认同)

Clo*_*ble 6

我将在第一个if中作为参数提供给分离函数的决定,然后决定执行哪个代码,例如:

if(conditionX)
{
    Method1(Condition Parameters)
}
else if(conditionY)
{
    Method1(Condition Parameters)
}
Run Code Online (Sandbox Code Playgroud)

另一种方法是向决策方法(矩阵)提供所有需要的信息,此方法返回一个整数,您在switch语句中使用该整数来决定执行哪个代码.通过这种方式,您可以将desicion逻辑分开,使其可读并且在需要时可以轻松进行单元测试:

DecisionMatrix(conditionX, conditionY, condition1, condition2)
{
  //  return a value according to the conditions for Example:
  // CoditionX + Condition1 => return 1
  // CoditionX + Condition2 => return 2
  // CoditionY + Condition1 => return 3
  // CoditionY + Condition2 => return 4
}

switch(DecisionMatrix)
{
    case 1: //run code X1       
    break;
    case 2: //run code X2
    break;
    case 3: //run code Y1       
    break;
    case 4: //run code Y2
    break;
}
Run Code Online (Sandbox Code Playgroud)