2011-06-08 282 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) 
    ; 

}