2

私は現在、Java で書かれた LISP インタープリターに取り組んでいます。今、私はクロージャーにこだわっています。次のようなクロージャを有効にしたい:

(define a 1000)
(define closure (lambda (a) (lambda (b) (+ a b))))
(define x (closure 10))
(x 20) --> 30

したがって、(x 20)を返す必要があり30ます。しかし、私のインタープリターでは 1020 が返されます。間違いは私のラムダクラスにあると思います。次のようになります。

public class LLambda extends LOperation {

    private LList parameters;
    private LList definitions;

    public LLambda(LList parameters, LList definitions) {

        this.parameters = parameters;
        this.definitions = definitions;
    } 

    public LObject eval(Environment environment, LObject tokens) {

        environment = environment.copy();

        for(int i = 0; i < parameters.size(); i++) {

            LSymbol key = LSymbol.create(parameters.get(i));
            LObject object = ((LList) tokens).get(i);
            object = object.eval(environment, tokens);  
            environment.put(key, object);
        }

        return definitions.eval(environment, tokens);
    }
}

このクラスは問題なく動作しますが、クロージャーを有効にするための環境値を保存しません。誰かがそれを行う方法を考えていますか? そして、どこでそれをするのですか?コンストラクターまたは eval メソッドで?

そして、これを実行しないと:

environment = environment.copy();

クロージャーは機能しますが、他のいくつかのテストに違反します。

ありがとう。

(ソース全体をアップロードするか、GIT で無料で提供することもできます)。

4

2 に答える 2

4

このクラスは問題なく動作しますが、クロージャーを有効にするための環境値を保存しません。誰かがそれを行う方法を考えていますか? そして、どこでそれをするのですか?コンストラクターまたは eval メソッドで?

はい、クラスは環境を保存する必要があります。一般的に言えば、メンバー変数。:)

環境は評価時ではなく、ラムダの構築時にバインドされるため、コンストラクター内にある必要があります。

評価時には、元の環境は利用できません。新しい環境は利用できます。

方言が純粋にレキシカル スコープである場合、ラムダに環境パラメーターは必要ありません。ラムダとは何か覚えていますか? 関数です。フォームの評価には環境が必要です。関数の評価はそうではありません。関数の評価は関数呼び出しであり、引数だけを取ります。環境は関数に渡されません。関数本体は、独自のプライベート環境を持つカプセル化された空間で評価されます。(evalラムダに関数が存在することさえ間違っているように思えます。これに名前を付けcallたり、そのようなものにしたい場合があります。解釈されたラムダは評価器のサービスを使用しますが、それは 1 つではありません。)

ラムダ内では、アクションはラムダ本体のフォームを評価することになります。それらは保存された環境を使用します(渡されたものではありません)。

ラムダ パラメータが引数値にバインドされる環境を確立する必要があります。これは、キャプチャされた環境内にネストされています。(つまり、 と呼ばれる引数は、 と呼ばれるxキャプチャされた変数を隠しますx)。

(環境をネストする何らかの方法が既に必要です。つまり、外部環境を参照するいくつかの新しいバインディングを構築します。)

レキシカルに加えて動的スコープをサポートしたい場合は、そのための環境を渡す必要はありません。暗黙的に実行できます。スレッドローカル変数は、動的環境などを維持できます。

(詳細はインタープリターの設計によって異なります。一部の設計では、(インタープリターを表す) コンテキスト オブジェクトが常に渡されます。たとえば、明示的なスタックを持つバイト コード仮想マシンに移動する場合は、それが必要になります。)

于 2012-04-17T04:10:02.370 に答える
2

Christian Queinnec の著書Lisp in Small Piecesを読むことを強くお勧めします。この本では、Lisp (またはスキームのような) 評価器、インタプリタ、コンパイラを実装する多くの方法が詳細に説明されています。

ローカル値とは異なる閉じた値を処理する必要があります。

于 2012-04-15T15:05:49.307 に答える