2011-06-08 283 views
6

如果像下面那样有一个大的(约100加)if else语句,if else条件可能是不规则的(例如一些取决于3个变量,一些取决于4),是否有任何方法使它简单?Big if else语句

基本上我有一个约100加行的表,其中a,b,c和d为列。基于a,b,c和d,我需要执行3种不同类型的功能。

该表描述了一组业务规则。

uint8 a; 
uint8 b; 
uint16 c; 
uint8 d; 

if  (a == 1 && b == 1   && c == 0)   { functionA();} 
else if (a == 5 && b == 5   && c == 2 && d == 2) { functionB();} 
else if (a == 1 && (b ==36 || b == 7) && c == 0)   { functionC();} 
else if (a == 3 && b == 3      && d == 50) {functionA();} 
    : 
    : 
+7

为什么在这个世界上,你会得到像这样的“if ... else”陈述? – Blender 2011-06-08 02:43:57

+2

这将是很好,如果你解释你正在努力完成 – GWW 2011-06-08 02:44:39

+0

我有一个列的a,b,c和d列表。根据列的值,它需要更新一些变量。 – leslieg 2011-06-08 02:47:06

回答

1

分裂它根据你的4个变量

if(a==1) 
{ 
    if(b==1) 
    { 

    } 
    else if(b==3) 
    { 

    } 
} 
else if(a==3) 
{ 

} 

这将使它更简单一点阅读并遵照

+3

甚至可能使用'switch'。 – 2011-06-08 02:48:14

+0

'switch ... case'比较习惯。对于像uint8_t这样的有限范围的类型,编译器甚至可能使它成为一个跳转表...... – Nemo 2011-06-08 02:49:44

7

有很多方法,使其更简单,例如:

  • 您可以填充从一个结构图保持abcd值来检查要调用的函数(填充地图的代码可能仍然是一团糟,但它会更快更清晰地执行;可以添加两个键的情况ala b == x || b == y
  • 你可以手动因素的条件:给你的例子,if (a == 1) if (b == 1 && c == 0) functionA(); else if ((b == 36 || b == 7) && c == 0) functionC();。陈述可能会使这个更清洁。在这种分解中,您还可以使用<,<=,>和/或>=划分更大的搜索空间,从而将性能从O(N)提高到O(log2N)。
  • 对于常见的简单情况下的测试a,b,cd一旦使用宏ala #define ELIF_ABCD(A, B, C, D, F) else if (a == (A) && b == (B) && c == (C) && d == (D)) F();。根据需要为其他测试组合添加宏,例如ABDABCAD。 (可能使代码更加神秘),但是可以探索比特移位和将这些值ORing在一起足够大的类型(int64_t),然后例如二进制搜索功能指针数组

东西看出来的是,虽然在的if/else链可能包含的东西,如:

if (a == 1 && c == 3 && d == 2) functionY(); 
else if (a == 1 && b == 2 && c == 3) function X(); 

这里,顺序是显著为输入可以同时匹配。如果搜索有不同的因素,或者使用了某种对函数指针进行索引的方式,这个方面很容易丢失或改变,这是赞成上述宏观方法的一个参数。

+0

只用if语句就可以使用它,并且你可以在所有ifs中使用它:IF(1,2,3,5 ,if(1,2,3,4,bar)' – 2011-06-08 02:58:52

+0

@Martinho:很多选择 - 另一个吸引人的就是'#define ABCDF(...)if(...){F();返回; }'...完全摆脱'if' /'else'链。 – 2011-06-08 03:00:56

0

没有进一步的细节,人们只能猜测如何简化它。

一种可能性是使用布尔变量。如果您经常评估某些组合,则可以使用布尔值来保存该重新评估。

如果有一组固定的条件,您也可以对int使用位掩码,然后执行case

但是,这些只是猜测,不知道你真正在做什么的更多细节。

3

根据Tony的建议使用地图,你可能会优化一下。

您可以将所有4个数字编码为单个uint64_t(或更小,具体取决于其值的范围)。

uint64_t h = (a << 32) + (b << 24) + (c << 8) + d; 

然后,您可以构建一个将散列映射到函数指针的std::map<uint_64_t, void (*)()>。尽管构建地图可能需要一些努力。我认为让大家听取其他建议并重构代码会更好。

+0

这与我的第4条建议很相似,不过我建议使用散列来搜索数组/矢量。更多地考虑它,当一个或多个变量未被测试时,存在处理案例的问题 - 可能需要针对每个搜索哈希具有掩码值,并且按位与掩码与a,b, c,d将结果与搜索哈希进行比较。 – 2011-06-08 03:14:52

+0

@Tony:我们可能在同一时间张贴了,我猜想地图,因为我认为会有大量未使用的点击。我认为矢量会太大。 – GWW 2011-06-08 03:18:22

+0

@GWW:'vector'可以打包并进行二进制搜索,所以消耗的内存比map更少(取决于实现,但我认为是1/2到1/10),并且查找速度稍快。 (稀疏阵列对虚拟内存的浪费最大,但是对于40位密钥,我也对这里的使用感到不舒服)。 – 2011-06-08 03:24:49

1

我会考虑类似的东西 - 它保留了函数的条件,并使整个测试更容易测试和扩展(在我看来)。

您可能会生成采用构造函数参数的子类,以减少所需类的总数。

class ICase 
{ 
    virtual ~ICase() {} 
    virtual bool matches_arguments(int a, int b, int c) const =0; 
    virtual void do_it(int a, int b, int c)=0; 
}; 

class CaseA : public ICase 
{ 
    bool matches_arguments(int a, int b, int c) const { return (a == 1 && b == 1   && c == 0); } 
    bool do_it(int a, int b, int c) { functionA(); } 
}; 

... 
//Some setup - only need to do this once 
std::vector< shared_ptr<ICase> > cases; 
cases.push_back(new CaseA); 
cases.push_back(new CaseB); 

//The conditionals 
for(int i=0; i<cases.size(); ++i) 
{ 
    if(cases[i]->matches_arguments(a,b,c)) { cases[i]->do_it(a,b,c); break; } 
} 
+0

哦,当然你应该为你的课程使用描述性和明智的名字 - 这将有助于理解堆。 – 2011-06-08 02:58:55

+1

这将使if-else链变得简单,但是......它不会将恶梦移入Case类吗? – 2011-06-08 03:03:49

+0

可能。然而,它通常更容易重构以利用案例之间的相似性ii)更容易测试每个案例iii更容易地添加新案例iv更容易理解每​​个案例如果命名得很好, - 有多少(如果有的话)适用于原始帖子将取决于“if”混乱的真正底层结构。 – 2011-06-08 03:12:59

1

扩大托尼的第一点:

您可以填充从结构一图保持A,B,c和d值检查到函数调用

总结所有的变量向上的状态对象或东西:

struct state { 
    uint8 a; 
    uint8 b; 
    uint16 c; 
    uint8 d; 
} 

并添加了一堆的茨艾伦È可能的状态到列表:

std::set<state> functionASet; 
functionASet.insert(aState); 
... 

然后测试该组是否包含用于a, b, c, d的电流值的构成的状态:

// init a state struct with current values for a, b, c, d 
if(functionASet.find(currentValues) != functionASet.end()) 
    functionA(); 
else if(functionBSet.find(currentValues) != functionASet.end()) 
    functionB(); 
else ... 

OR,状态添加到地图:

typedef void (*func)(); 

std::map<state, func> functionMap; 

只需调用与找到的状态相匹配的函数即可:

std::map<state, func>::iterator search = functionMap.find(currentValues); 
if(search != functionMap.end()) 
    (search->second())(); 
+0

与此相关的问题是某些列不关心。例如,第一个条件d有一个不在意的地方。您需要创建256个状态对象(每个可能的d值一个)来覆盖第一个条件。 – ThomasMcLeod 2011-06-08 04:04:40

+0

一致认为,这并不理想。 – darvids0n 2011-06-08 07:11:10

1

要正确高效地完成此操作,首先需要将每行的表示标准化并将其转换为可进行索引或排序的紧凑数据。您可以尝试通过简单地将列的值序列化为固定长度的字符串,然后将该字符串和适当函数的指针插入到映射中,其中字符串作为键,函数指针作为值。

但是,问题有点复杂,因为在某些行中某些列不计数,它们是“不关心”。假设每列中没有值可以充当“无所谓”值,除了每列的值之外,关键字还必须包含指示哪些列是重要的数据。我们可以通过在包含一个位掩码的字符串上附加一个额外的字节来指明哪些列是重要的。为了使地图搜索在这种情况下正常工作,不重要的列必须始终在键中包含相同的值(零是一个不错的选择)。

现在我们只写一个函数来从我们表的每一行的列中构造一个六字节的键。使用此功能在地图构建完成后执行初始地图插入和查找。

这种方法查找速度很快,O(log n),其中n是行数。

+0

这里的问题是:1)当搜索a,b,c,d时,我们不知道哪些值可能是“不在乎”,所以我们需要尝试所有值的组合不关心为了找到所有可能的相关映射条目2)这并不保留在现有的if/else代码中可以匹配相同输入的多个测试的排序。如果darvids0n的更简单的实现避免不关心支持填充所有组合的值,在实践中证明过于笨拙,我仍然对如何更好地处理这个问题感到不知所措。 – 2011-06-08 05:44:05

+0

@Tony,我可能不正确,但我假定OP的if-else语句是第一次尝试,并且示例和OP数据表中的if-else语句的排序并不重要。这也假定数据中没有冲突,即,a,b,c,d空间跨功能A,B,C划分 - 功能A,B,C的标准是相互排斥的。如果不是这种情况,那么可以使用布尔代数约简对数据进行预处理,以便根据某些条件(例如原始数据表中的顺序)来解决冲突。 – ThomasMcLeod 2011-06-08 14:00:32

0

看来真的很乱:/

当你需要描述业务规则,你应该使用描述方法,而不是一个必要途径。它更具可读性,适应规则通常要容易得多。

我的第一个想法是使用一个表,由a,b,c和d索引,并且内部有函数指针(或函子)。

初始化代码会有点可怕,我的建议是保持它的字典顺序:

// Note: you don't have to initialize the null values if the variable is static 
Table[0][0][0][1] = functionA; 
Table[0][3][0][1] = functionB; 
... 

检索功能则是小菜一碟,只记得测试无效,如果这是可能的没有功能(否则为assert)。

另一种解决办法是选择分解为若干步骤,使用功能:

  • 开关a,选择要调用的函数(使用default来处理的情况下,你不关心a
  • 开关bcd ....

实施例:

void update(int a, int b, int c, int d) { 
    switch(a) { 
    case 0: updateA0(b, c, d); break; 
    case 1: updateA1(b, c, d); break; 
    default: updateAU(b, c, d); break; 
    } 
} 

void updateA0(int b, int c, int d) { 
    switch(b) { 
    case 0: updateA0B0(c, d); break; 
    case 1: updateA0B1(c, d); break; 
    default: updateA0BU(c, d); break; 
    } 
} 

// etc... 

它更容易“跟随”更新链,并应用本地更新。此外,适用于每个子功能的逻辑,很容易应用范围选择(if (b >= 5 && b < 48)),而不会“打破”模式或复制初始化。最后,根据某些路径的可能性,如果易于选择,则可以在updateA1中轻松选择开启d

它至少与您当前的解决方案一样灵活,但更具可读性/可维护性。

1

梦到这个问题一夜之间..并想出了一个巧妙的解决办法

如果乱变像这样的核心(由谷歌测试ilbraries使用的匹配系统的启发) - 我认为这是相当漂亮。

Params(1,2,3,4) 
    .do_if(match(1,_,3,5), functionA) 
    .do_if(match(1,_,3,4), functionB) 
    .do_if(match(_, OR(2,3),3,5), functionC) 
// .do_if(match(1,_,4,6)|match(3,_,5,8)), functionD) 
    ; 

最后一行我还没有实现。 _意味着匹配任何数字,OR意味着比赛是(虽然你可以嵌套它OR(1,OR(2,3))应该罚款。

的支持代码剩下的就是模板函数乱七八糟,使这项工作。如果有兴趣我可以发表更多彻底描述发生了什么......但它并不过分复杂 - 只要很长时间,我希望它可以被清理一些。

大概可以拉出,并归纳为一个漂亮的图书馆太 - 虽然我可能会看适应谷歌的测试代码,而不是任何基础关闭此代码;)

struct RawParams 
{ 
    RawParams(int a, int b, int c, int d) : a_(a), b_(b), c_(c), d_(d) {} 
    int a_,b_,c_,d_; 
}; 

struct ParamsContinue 
{ 
    RawParams * p_; 

    ParamsContinue() : p_(0) {} 
    ParamsContinue(RawParams * p) : p_(p) {} 

    template<typename CONDITION, typename FN> 
    ParamsContinue do_if(CONDITION cond, FN fn) 
    { 
    if(!p_) { return ParamsContinue(); } 
    if(cond(p_->a_,p_->b_,p_->c_,p_->d_)) { fn(); return ParamsContinue(); } 
    return *this; 
    } 
}; 

struct Params 
{ 
    Params(int a, int b, int c, int d) : params_(a,b,c,d) {} 
    RawParams params_; 

    template<typename CONDITION, typename FN> 
    ParamsContinue do_if(CONDITION cond, FN fn) 
    { 
    return ParamsContinue(&params_).do_if(cond,fn); 
    } 
}; 

struct AnySingleMatcher 
{ 
    bool operator()(int i) const { return true; } 
}; 

AnySingleMatcher _; 

template<typename M1, typename M2, typename M3, typename M4> 
struct Match 
{ 
    Match(M1 in_m1, M2 in_m2, M3 in_m3, M4 in_m4) : 
    m1(in_m1), 
    m2(in_m2), 
    m3(in_m3), 
    m4(in_m4) 
    {} 

    bool operator()(int a, int b, int c, int d) const { return m1(a)&&m2(b)&&m3(c)&&m4(d); } 

    M1 m1; 
    M2 m2; 
    M3 m3; 
    M4 m4; 
}; 

struct AnyMatcher {}; 
struct IntMatcher 
{ 
    IntMatcher(int i) : i_(i) {} 
    bool operator()(int v) const { return v==i_; } 
    int i_; 
}; 

template<typename T> 
struct as_matcher 
{ 
    typedef T type; 
    static T as(T t) { return t; } 
}; 

template<> 
struct as_matcher<int> 
{ 
    typedef IntMatcher type; 
    static IntMatcher as(int i) { return IntMatcher(i); } 
}; 

template<typename M1, typename M2, typename M3, typename M4 > 
Match< typename as_matcher<M1>::type, typename as_matcher<M2>::type, typename as_matcher<M3>::type, typename as_matcher<M4>::type > 
match(M1 m1, M2 m2, M3 m3, M4 m4) 
{ 
    return 
    Match< typename as_matcher<M1>::type, typename as_matcher<M2>::type, typename as_matcher<M3>::type, typename as_matcher<M4>::type >(
     as_matcher<M1>::as(m1), as_matcher<M2>::as(m2), as_matcher<M3>::as(m3), as_matcher<M4>::as(m4)); 
}; 

template<typename T1, typename T2> 
struct OrMatcher 
{ 
    OrMatcher(T1 t1, T2 t2) : t1_(t1), t2_(t2) {} 
    T1 t1_; 
    T2 t2_; 
    bool operator()(int i) const { return t1_(i) || t2_(i); } 
}; 

template<typename T1, typename T2> 
OrMatcher< typename as_matcher<T1>::type, typename as_matcher<T2>::type > OR(T1 t1, T2 t2) 
{ 
    return OrMatcher< typename as_matcher<T1>::type, typename as_matcher<T2>::type >(as_matcher<T1>::as(t1),as_matcher<T2>::as(t2)); 
}; 

#include <iostream> 
void functionA(){ std::cout<<"In A"<<std::endl;}; 
void functionB(){ std::cout<<"In B"<<std::endl;}; 
void functionC(){ std::cout<<"In C"<<std::endl;}; 
void functionD(){ std::cout<<"In D"<<std::endl;}; 

int main() 
{ 
    Params(1,2,3,4) 
    .do_if(match(1,_,3,5), functionA) 
    .do_if(match(1,_,3,4), functionB) 
    .do_if(match(_, OR(2,3),3,5), functionC) 
// .do_if(match(1,_,4,6)|match(3,_,5,8)), functionD) 
    ; 

}