2012-11-16 95 views
0

我有一個處理元素隊列的while (!Queue.empty())循環。有一系列從最高優先級到最低優先級的模式匹配器。匹配模式時,相應的元素將從隊列中移除,並從頂部重新開始匹配(以便優先級最高的匹配器有機會首先執行)。如何重構此while循環以擺脫「繼續」?

所以現在它看起來像這樣(簡化版本):

while (!Queue.empty()) 
{ 
    auto & Element = *Queue.begin(); 

    if (MatchesPatternA(Element)) { // Highest priority, since it's first 
     // Act on it 
     // Remove Element from queue 
     continue; 
    } 
    if (MatchesPatternB(Element)) { 
     // Act on it 
     // Remove Element from queue 
     continue; 
    } 
    if (MatchesPatternC(Element)) { // Lowest priority, since it's last 
     // Act on it 
     // Remove Element from queue 
     continue; 
    } 

    // If we got this far, that means no pattern was matched, so 
    // Remove Element from queue 
} 

這工作,但我想重構這個循環中的一些方法,以消除使用關鍵字continue的。

爲什麼?因爲如果我想外部匹配一個外部函數的模式,它顯然會中斷。例如。

void ExternalMatching(...) 
{ 
    if (MatchesPatternB(Element)) { 
     // Act on it 
     // Remove Element from queue 
     continue;  // This won't work here 
    } 
} 

while (!Queue.empty()) 
{ 
    auto & Element = *Queue.begin(); 

    if (MatchesPatternA(Element)) { 
     // Act on it 
     // Remove Element from queue 
     continue; 
    } 
    ExternalMatching(...); 
    if (MatchesPatternC(Element)) { 
     // Act on it 
     // Remove Element from queue 
     continue; 
    } 

    // If we got this far, that means no pattern was matched, so 
    // Remove Element from queue 
} 

我不希望有寫重複的,如果像if (ExternalMatching(...)) { ... continue; }聲明,我寧願找到表達這種邏輯更清潔的方式。

這種簡化的例子可能使它看起來像一個好主意,使模式匹配更普遍的,而不是不同MatchesPatternAMatchesPatternBMatchesPatternC等功能。但在我的情況下,模式非常複雜,我還沒有準備好推廣它們。所以我想保留那部分,分開功能。

任何優雅的想法?謝謝!

+5

呃,把'if's改成'else if's並把最後一個位包裝在'else'中?這基本上是你繼續做的。 – Yuushi

+0

嗯,或者你是對的,或者我簡化的例子太簡單了,這使得這裏可行,而不是我真正的問題。讓我找出哪個。 –

+0

好吧,所以我的情況稍微複雜一點(而不是布爾返回值,我有3個可能的返回值),但我仍然認爲你的想法很好。我想我可以把'else if's放在那裏。謝謝! –

回答

2

如果您有權訪問C++ 11,我想推薦其他解決方案。基礎我創建了一個可以在運行時調整的處理程序和操作的容器。根據您的要求,它可能是您的設計的專家或認同。這是它:

#include <functional> 

typedef std::pair<std::function<bool(const ElementType &)>, 
        std::function<void(ElementType &)> > HandlerData; 
typedef std::vector<HandlerData> HandlerList; 


HandlerList get_handlers() 
{ 
    HandlerList handlers; 
    handlers.emplace_back([](const ElementType &el){ return MatchesPatternA(el); }, 
         [](ElementType &el){ /* Action */ }); 
    handlers.emplace_back([](const ElementType &el){ return MatchesPatternB(el); }, 
         [](ElementType &el){ /* Action */ }); 
    handlers.emplace_back([](const ElementType &el){ return MatchesPatternC(el); }, 
         [](ElementType &el){ /* Action */ }); 
    return handlers; 
} 


int main() 
{ 
    auto handlers = get_handlers(); 
    while(!Queue.empty()) { 
    auto &Element = *Queue.begin(); 

    for(auto &h : handlers) { 
     // check if handler matches the element 
     if(h.first(Element)) { 
     // act on element 
     h.second(Element); 
     break; 
     } 
    } 

    // remove element 
    Queue.pop_front(); 
    } 
} 
+0

謝謝你這個充實的答案。 我認爲從長遠來看(一旦我當前的if-else-if-else-if解決方案變得無法維護,或者我需要在運行時更改匹配器/操作),這絕對是一種方式。 很容易擴展它以支持'ExternalMatching()':只需修改'get_handlers()'來調用'External_add_handlers()',用它們的處理程序填充HandlerList。優先順序被保留。 非常感謝! –

+0

我會將此標記爲已接受,即使這不是我使用的解決方案(現在),但是因爲我認爲這是最好的答案。 –

+0

不錯的想法,但這裏似乎有點矯枉過正。但是如果需要在運行時更改「處理程序」,則非常靈活。 – hirschhornsalz

2

考慮的接口:

class IMatchPattern 
{ 
public: 
    virtual bool MatchesPattern(const Element& e) = 0; 
}; 

然後就可以實現組織IMatchPattern對象的容器,以允許迭代訪問每個模式匹配方法。

+1

+1,但需要「虛擬」。另外我想你想從方法名的末尾刪除'B'。 –

+0

@j_random_hacker謝謝!編輯我的答案以反映你的建議。 –

+1

是的,我認爲從長遠來看這是一個好主意。 我可以有一個std ::向量 >>或其他東西。基本上是一對模式匹配器和相關的動作。 謝謝。 –

2

我會建議使用,做的模式匹配(但對結果不作爲)一個函數,然後一組作用於不同的選項功能:

enum EventType { 
    A, B, C //, D, ... 
}; 

while (!queue.empty()) { 
    auto & event = queue.front(); 
    EventType e = eventType(event); // Internally does MatchesPattern* 
            // and returns the match 
    switch (e) { 
    case A: 
     processA(event); 
     break; 
    case B: 
     processB(event); 

這樣,你清楚地分開從處理匹配,循環只是一個簡單的調度程序

+0

嗯,我必須考慮這一點。謝謝! –

+0

我對此的一個擔心是它可能會使其添加/更改模式和相關操作變得更加冗長(即必須添加新的枚舉EventType值等)。如果是這樣的話,我必須確保它是值得的。但它可能仍然是一個好主意。 –

1

您可以更改您的ExternalMatching返回bool,表示處理已完成。這樣,來電者將能夠繼續評估是否有必要:

bool ExternalMatching(...) 
{ 
    if (MatchesPatternB(Element) { 
     // Act on it 
     // Remove Element from queue 
     return true; 
    } 
    return false; 
} 

現在你可以這樣調用:

if (ExternalMatchin1(...)) continue; 
if (ExternalMatchin2(...)) continue; 
... 
if (ExternalMatchingN(...)) continue; 
+0

是的,我提到這是一個潛在的解決方案,但我想看看是否有其他更好的方法來解決這個任務。 另一種解決方案(我不想使用)是用'throw'替換'continue's,它可以跨越函數來工作,但是使用throw來進行邏輯似乎不是一個好主意(應該是用於很少發生的錯誤等)。 謝謝。 :) –

0

我想建議,將採取元素,並創建一個工廠函數一個合適的處理程序並將接口指針返回給處理程序。

while (!Queue.empty()) 
{ 
    auto & Element = *Queue.begin(); 
    // get the appropriate handler object pointer e.g. 
    IPatternHandler *handler = Factory.GetHandler(Element); 
    handler->handle(); 
    // clean up handler appropriately 
} 
+0

使用接口正朝着正確的方向發展,但您剛剛將如何從原始循環中選擇正確處理程序的問題轉移到了'Factory.GetHandler()'中。我認爲邁克爾Sh釘了它。 –

+0

@j_random_hacker:問題在於處理模式時的'continue'。我只是將實例化適當的處理程序對象的問題推入工廠。這分開了兩個問題。創建處理程序並使用處理程序 – Chubsdad

+0

好,但是'Factory.GetHandler()'如何選擇正確的處理程序?隨着一堆'如果...別人'?還不如呢?不是嗎? –

1

好吧,我結束了重寫循環更類似於這個。

巨大的感謝和信用去Yuushi,dasblinkenlight,大衛羅德里格斯的幫助;這個答案是基於他們的答案的組合。

bool ExternalMatching(...) 
{ 
    bool Match; 

    if ((Match = MatchesPatternX(Element))) { 
     // Act on it 
    } else if ((Match = MatchesPatternY(Element))) { 
     // Act on it 
    } 

    return Match; 
} 

while (!Queue.empty()) 
{ 
    auto & Element = Queue.front(); 

    if (MatchesPatternA(Element)) { // Highest priority, since it's first 
     // Act on it 
    } else if (MatchesPatternB(Element)) { 
     // Act on it 
    } else if (ExternalMatching(...)) { 
    } else if (MatchesPatternC(Element)) { // Lowest priority, since it's last 
     // Act on it 
    } 

    // Remove Element from queue 
} 

現在,我知道有進一步提升的空間,看到Mateusz Pusz和邁克爾噓的答案。但是,這足以回答我原來的問題,現在就可以做到。我會考慮在將來改進它。

如果你好奇,想看看真正的代碼(非精簡版),在這裏看到:

https://github.com/shurcooL/Conception/blob/38f731ccc199d5391f46d8fce3cf9a9092f38c65/src/App.cpp#L592

再次感謝大家!

+0

我剛開始寫一個答案「用一個簡單的'else if'來代替...」,想知道爲什麼沒有人提出這個答案。無論如何,你自己找到了它。 – hirschhornsalz

+0

順便說一下,我認爲'continue'本身沒有問題,但大多數人似乎更習慣於'else if'成語,並且能夠更快更容易地解析它。你仍然可以通過'continue'使用'ExternalMatching'。 – hirschhornsalz