如何重构这个while循环以摆脱"continue"?

How to refactor this while loop to get rid of "continue"?

本文关键字:循环 continue while 何重构 重构      更新时间:2023-10-16

我有一个处理元素队列的while (!Queue.empty())循环。有一系列从最高优先级到最低优先级的模式匹配器。当一个模式匹配时,相应的元素会从队列中删除,然后从顶部重新开始匹配(这样优先级最高的匹配者就有机会首先采取行动)。

所以现在它看起来像这样(一个简化版本):

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; }这样的重复if语句,我宁愿找到一种更干净的方式来表达这种逻辑。

这个简化的示例可能会使模式匹配看起来更通用,而不是具有不同的MatchesPatternAMatchesPatternBMatchesPatternC等函数。但在我的情况下,模式相当复杂,我还没有准备好对它们进行概括。所以我想保持这个部分的原样,独立的函数。

有什么好主意吗?非常感谢。

如果您可以访问C++11,我想推荐另一种解决方案。Basicaly我创建了一个处理程序和操作的容器,可以在运行时进行调整。根据您的要求,它可能对您的设计有利或不利。这是:

#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();
}
}

我建议使用一个进行模式匹配(但不对结果进行操作)的函数,然后使用一组对不同选项进行操作的函数:

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);

通过这种方式,您可以清楚地将匹配与处理分开,循环只是一个简单的调度器

考虑一个接口:

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

然后,您可以组织一个实现IMatchPattern的对象容器,以允许对每个模式匹配方法进行迭代访问。

您可以更改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;

好的,我最终重写了与此类似的循环。

非常感谢Yuushi、dasblinkenlight和David Rodríguez的帮助;这个答案是基于他们答案的组合。

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和Michael Sh的回答。然而,这足以回答我最初的问题,现在就可以了。我将来会考虑改进它。

如果你想看看真实的代码(非简化版本),请参阅此处:

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

再次感谢大家!

我建议使用一个Factory函数,该函数将接受Element并创建一个适当的处理程序,并将接口指针返回到处理程序。

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
}