1

我有一个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 语句if (ExternalMatching(...)) { ... continue; },我宁愿找到一种更简洁的方式来表达这个逻辑。

MatchesPatternA这个简化的示例可能使模式匹配更通用而不是具有不同的 , MatchesPatternB,MatchesPatternC等函数似乎是一个好主意。但在我的情况下,模式非常复杂,我还没有准备好概括它们。所以我想保持那部分原样,单独的功能。

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

4

6 回答 6

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

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

于 2012-11-16T04:47:00.867 回答
3

如果您可以访问 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();
  }
}
于 2012-11-16T07:43:47.250 回答
2

考虑一个接口:

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

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

于 2012-11-16T04:44:09.703 回答
1

您可以将您的更改ExternalMatching为 return 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;
于 2012-11-16T04:47:09.437 回答
1

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

非常感谢和感谢 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

再次感谢大家!

于 2012-11-16T08:00:08.130 回答
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
}
于 2012-11-16T04:53:32.927 回答