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、、、などの関数を使用するのではなく、パターンマッチングをより一般的にすることをお勧めします。しかし、私の状況では、パターンは非常に複雑であり、まだ一般化する準備ができていません。ですから、その部分をそのままにして、別々の機能にしたいのです。MatchesPatternBMatchesPatternC

エレガントなアイデアはありますか?ありがとうございました!

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

わかりました、これに似たループを書き直すことになりました。

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 に答える
1

処理が完了したことを示すExternalMatchingreturnに変更できます。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 に答える
0

Element を受け取り、適切なハンドラーを作成し、インターフェイス ポインターをハンドラーに返す Factory 関数を提案したいと思います。

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 に答える