1. ホーム
  2. c++

C++における遅延評価

2023-10-27 13:16:54

質問

C++は遅延評価のネイティブサポートを持っていません(Haskellがそうであるように)。

私は、合理的な方法で C++ に遅延評価を実装することが可能であるかどうか疑問に思っています。もし可能であれば、どのようにそれを行うのでしょうか?

EDIT: Konrad Rudolphの回答が好きです。

例えば、matrix_addがmatrixに対して動作するように、Tに対して本質的に動作するパラメトリッククラスのlazyを使用するなど、より汎用的な方法で実装できないものかと考えています。

Tに対するどんな操作も、代わりにlazyを返すでしょう。唯一の問題は、引数と操作コードをlazy自身の中に格納することです。どなたか、これを改善する方法をご存知でしょうか?

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

<ブロッククオート

C++で遅延評価を合理的な方法で実装することが可能かどうか疑問に思っています。もし可能であれば、どのようにそれを行うのでしょうか?

はい、これは可能であり、例えば行列計算のために非常によく行われます。これを容易にする主なメカニズムは演算子のオーバーロードです。行列の足し算の場合を考えてみましょう。関数のシグネチャは通常次のようなものです。

matrix operator +(matrix const& a, matrix const& b);

さて、この関数を遅延させるためには、実際の結果の代わりにプロキシを返せば十分です。

struct matrix_add;

matrix_add operator +(matrix const& a, matrix const& b) {
    return matrix_add(a, b);
}

あとはこのプロキシを書くだけです。

struct matrix_add {
    matrix_add(matrix const& a, matrix const& b) : a(a), b(b) { }

    operator matrix() const {
        matrix result;
        // Do the addition.
        return result;
    }
private:
    matrix const& a, b;
};

マジックは、メソッド operator matrix() からの暗黙の変換演算子です。 matrix_add からプレーンな matrix . このようにして,複数の処理を連鎖的に行うことができる(もちろん,適切なオーバーロードを提供することで).評価が行われるのは、最終的な結果が matrix インスタンスに割り当てられたときのみ行われます。

EDIT もっと明確に言うべきでした。そのままでは、評価は遅延的に行われるものの、同じ式の中で行われるため、このコードは意味をなしません。特に、別の追加でこのコードが評価されるのは matrix_add 構造を変更して連鎖的な追加を可能にしない限り、別の追加によってこのコードが評価されます。C++0x では、可変長テンプレート (つまり、可変長のテンプレート リスト) を許可することで、これを非常に容易にしています。

しかし、このコードが実際に直接的な利益をもたらす非常に単純なケースとして、次のようなものがあります。

int value = (A + B)(2, 3);

ここで、想定しているのは AB は2次元の行列であり、その参照はFortran記法で行われる。つまり、上記の計算では の要素を行列の和から取り出しています。もちろん、行列全体を足すのは無駄なことです。 matrix_add を使えばいいのです。

struct matrix_add {
    // … yadda, yadda, yadda …

    int operator ()(unsigned int x, unsigned int y) {
        // Calculate *just one* element:
        return a(x, y) + b(x, y);
    }
};

他にもいろいろな例があります。私は、少し前に関連するものを実装したことがあるのを今思い出しました。基本的に、私は固定された、事前に定義されたインターフェイスに従うべき文字列クラスを実装する必要がありました。しかし、私の特別な文字列クラスは、実際にはメモリに保存されていない巨大な文字列を扱っていました。通常、ユーザーは元の文字列から小さな部分文字列にアクセスするために、関数 infix . 私はこの関数を文字列型用にオーバーロードして、文字列への参照を保持するプロキシを、希望する開始位置と終了位置とともに返しました。この部分文字列が実際に使用されるときだけ、C API に照会して文字列のこの部分を取得しました。