1. ホーム
  2. c++

[解決済み] C++とPythonの等価なジェネレータパターン

2022-06-27 21:09:10

質問

私は、C++で模倣する必要があるいくつかのPythonコードの例を持っています。私は特定のソリューションを必要としません (共同ルーチン ベースの収量ソリューションなど、それらもまた許容される回答でしょうが)、私は単に何らかの方法でセマンティクスを再現する必要があります。

Python

これは基本的なシーケンスジェネレータで、実体化したバージョンを保存するには明らかに大きすぎます。

def pair_sequence():
    for i in range(2**32):
        for j in range(2**32):
            yield (i, j)

目標は、上記のシーケンスの2つのインスタンスを維持し、半ロックステップで、しかしチャンクでそれらを繰り返し処理することです。以下の例では first_pass はバッファを初期化するためにペアのシーケンスを使用します。 second_pass を再生成します。 同じ正確なシーケンス を再生成し、バッファを再び処理します。

def run():
    seq1 = pair_sequence()
    seq2 = pair_sequence()

    buffer = [0] * 1000
    first_pass(seq1, buffer)
    second_pass(seq2, buffer)
    ... repeat ...

C++

C++での解決策としては、以下のような模倣しかありません。 yield を C++ のコルーチンで模倣することですが、これを行う方法についての良いリファレンスは見つかっていません。また、この問題に対する代替(一般的でない)解決策にも興味があります。私は、パス間でシーケンスのコピーを保持するのに十分なメモリ予算を持っていません。

どのように解決するのですか?

ジェネレータはC++でも別の名前で存在しています。 入力イテレータ . 例えば std::cin のジェネレータを持つのと同じです。 char .

単純にジェネレータが何をするものかを理解する必要があります。

  • データの塊があり、ローカル変数で 状態
  • init メソッドがあります。
  • next"メソッドがあります。
  • 終了を通知する方法があります。

あなたの些細な例では、それは十分に簡単です。概念的には

struct State { unsigned i, j; };

State make();

void next(State&);

bool isDone(State const&);

もちろん、これをちゃんとしたクラスとしてラップします。

class PairSequence:
    // (implicit aliases)
    public std::iterator<
        std::input_iterator_tag,
        std::pair<unsigned, unsigned>
    >
{
  // C++03
  typedef void (PairSequence::*BoolLike)();
  void non_comparable();
public:
  // C++11 (explicit aliases)
  using iterator_category = std::input_iterator_tag;
  using value_type = std::pair<unsigned, unsigned>;
  using reference = value_type const&;
  using pointer = value_type const*;
  using difference_type = ptrdiff_t;

  // C++03 (explicit aliases)
  typedef std::input_iterator_tag iterator_category;
  typedef std::pair<unsigned, unsigned> value_type;
  typedef value_type const& reference;
  typedef value_type const* pointer;
  typedef ptrdiff_t difference_type;

  PairSequence(): done(false) {}

  // C++11
  explicit operator bool() const { return !done; }

  // C++03
  // Safe Bool idiom
  operator BoolLike() const {
    return done ? 0 : &PairSequence::non_comparable;
  }

  reference operator*() const { return ij; }
  pointer operator->() const { return &ij; }

  PairSequence& operator++() {
    static unsigned const Max = std::numeric_limts<unsigned>::max();

    assert(!done);

    if (ij.second != Max) { ++ij.second; return *this; }
    if (ij.first != Max) { ij.second = 0; ++ij.first; return *this; }

    done = true;
    return *this;
  }

  PairSequence operator++(int) {
    PairSequence const tmp(*this);
    ++*this;
    return tmp;
  }

private:
  bool done;
  value_type ij;
};

そうですね...C++の方が少し冗長かもしれませんね :)