重构 if-else-if 结构

Refactor if-else-if structure

本文关键字:结构 if-else-if 重构      更新时间:2023-10-16

如何重构以下C++代码?我在程序中使用 C++11

void f(int a, int b, int c, int d, int e, int f) {
  // MAX1..MAX6 are constants, N1..N6 are constants
    if( a > MAX1) {
        .. code block 1..
    }
    else if(b > MAX2) {
        .. code block 2..
    }
    else if(c > MAX3) {
        .. code block ..
    }
    else if(d > MAX4) {
        .. code block 3 ..
    }
    else if(e > MAX5) {
        .. code block 4 ..
    }
    else if(f > MAX6) {
        .. code block5 ..
    }
    else if(( a > N1) && ( b > N2)) {
            .. code block 6 ..
    }
    else if(( c > N3) && ( d > N4)) {
            .. code block 7 ..
    }
    else if (( e > N5) && ( f > N6)) {
            .. code block 8 ..
    }
    else if (( a > N1) && ( b > N2) && (d > N4)) {
            .. code block 9 ..
    }
    else if (..some combination of (a > N1) to (f > N6)...) {
            // there are more than 30 such combinations in my code
            .. code block XX ..
    }
    else {
            .... code block ....
    }

函数中最多有 64 个代码块,每个代码块都可以使用匿名函数或命名函数:

my_func_array[64] = {
    [CONDITION(1,0,0,0,0,0)] = { codeblock1 },
    [CONDITION(0,1,0,0,0,0)] = { codeblock2 },
     ...
};

宏基本上会将前 6 个输入连接到索引,基本上转换为:

 my_func_array[64] = {
    [32] = { ... },
    [16] = { ... },
 };

这意味着您不必以任何特定顺序输入条件...

运行时,您还必须评估所有条件:

 int condition = CONDITION(a < MAX1, b < MAX2, c < MAX2, ...);
 if (my_func_array[condition])
      my_func_array[condition]();
 else
 {
      // this second block should cover all the other cases
      int condition2 = CONDITION(a < N1, b < N2, c < N3, ... );
      if (my_func_array2[condition2])
          my_func_array2[condition2]();
 }

你所拥有的似乎不是不合理的,但如果你真的想重构,你可以创建一个枚举

枚举类 CaseSelect {CASE0, CASE1, CASE2};

然后创建一个看起来与当前 if 树非常相似的函数,每个代码块返回相应的枚举。

然后使用枚举创建一个 case 语句,每个枚举中都有适当的逻辑

它不会给你带来太多好处,但它将选择状态的逻辑与操作逻辑分开,这在多行语句的情况下可能对清晰度有所帮助

您当然可以从a fMAXN数组开始(只是一个起始草图,我可以用有关您的条件、代码块和用途的更多信息进行扩展):

const int MAX[6] = { ... };
const int N[6] = { ... };
const std::function funcs[6];
void f(int in[6])
{
    for(int i = 0; i < 6; ++i)
    {
        if(in[i] > MAX[i])
        {
            funcs[i]();
            break;
        }
    }
}
因此,

假设您有一个互斥整数范围(xi到yi)的离散数N,并且您想根据给定输入z的范围调用一些代码块,那么:

  • 您希望使用std::map二叉树来存储每个范围的开头和相应的 Lambda。
  • 调用 Z 上的std::lower_bound以查找候选范围
  • 然后对照 Z 检查该范围的上限。
  • 如果在里面,调用 lambda

这将给出 O(log(N)) 时间,而大 if-else 链的 O(n) 时间。

首先,我会按照 Mark B 的建议去掉前 6 个分支,但是

  • 而不是break我会return

  • 我会简单地使用函数指针,std::function是矫枉过正这里

  • 我会使用std::array和绑定检查而不是原始数组。

现在无聊的情况已经消失了,我会对剩余的分支应用相同的模式,但这需要一些技巧,因为这些条件是复合的。

我假设MAX1 > N1.我希望这是真的。

首先,我将构建一个十进制数,对所有参数进行编码:

int arg=10^5*(a>N1?1:0)+10^4*(b>N2?1:0)+10^3*(c>N3?1:0)+10^2*(d>N4?1:0)+10*(e>N5?1:0)+(f>N1?1:0)

我也会以类似的方式对条件进行编码。例如:条件(a > N1) && ( b > N2)变为arg >= 110000,等等。

如您所见,在此编码之后,您可以像前 6 个分支一样消除剩余的分支。

如果效率是一个主要问题,你可以用 2 的幂(位移,然后使用位掩码)做同样的技巧。我不做一些摆弄,所以我不能帮你,但它的工作方式相同,效率更高。可能和拥有那么长的if-else if链你现在拥有的东西一样有效,或者甚至可能更有效,因为你没有重复相同的比较。

希望这有帮助。

如果有很多规则,函数对象(例如:lambdas)可以在那里进行救援:

出于演示目的,我对其进行了一点简化,只有 3 个参数......

而且我认为非检查有一个安全值,在这种情况下,0(没有它可能会生活,但那样它看起来会更丑)。

#include <iostream>
#include <functional>
struct Rule {
    int a;
    int b;
    int c;
    std::function<int()> fun;
};
Rule rules[]{
    { 10, 0, 0, []() { std::cout << "First!"; return 0;} },
    { 0, 20, 0, []() { std::cout << "Second!"; return 1;} }
};
int f(int a, int b, int c) {
    for (Rule rule : rules) {
        if ((rule.a == 0 || a > rule.a)
         && (rule.b == 0 || b > rule.b)
         && (rule.c == 0 || c > rule.c))
            return rule.fun();
    }
    std::cout << "Not match!";
    return 2;
}
int main() {
    f(5, 23, 3);
};