如何重构这个循环以摆脱“继续”?

时间:2012-11-16 04:36:05

标签: c++ refactoring while-loop continue

我有一个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的使用。

为什么呢?因为如果我想将模式匹配外包给外部函数,它显然会中断。 E.g。

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等功能。但在我的情况下,模式非常复杂,我还没准备好对它们进行概括。所以我想保留那部分,分开的功能。

有什么优雅的想法吗?谢谢!

6 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:2)

考虑一个界面:

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

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

答案 2 :(得分:2)

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

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

答案 3 :(得分: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;

答案 4 :(得分:1)

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

巨大的感谢和归功于Yuushi,dasblinkenlight,DavidRodrí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

再次感谢大家!

答案 5 :(得分:0)

我想建议一个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
}