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。我认为错误出在我的 lambda 类中。它看起来像这样:

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 方法中?

是的,类应该存储环境。一般来说,一个成员变量。:)

它应该在构造函数中,因为环境是在构造 lambda 时绑定的,而不是在 eval 时绑定的。

在评估时,原始环境不可用:新环境可用。

如果您的方言是纯粹的词法范围,那么您的 lambda 不需要环境参数。记住,什么是 lambda?它是一个函数。表单的评估需要一个环境。函数的评估不;函数的评估是一个函数调用,它只需要参数。环境不传递给函数;功能体在具有自己私有环境的封装空间中进行评估。(在 lambda 上存在一个eval函数甚至看起来是错误的;您希望它被命名call或类似的名称。解释的 lambda使用评估器的服务;但它不是一个。)

在您的 lambda 中,操作将是评估 lambda 主体的形式。那些将使用存储的环境(不是传入的任何内容)。

您必须建立一个环境,其中 lambda 参数绑定到参数值。这嵌套在捕获的环境中。(即,一个称为x阴影的参数隐藏了一个名为 的捕获变量x)。

(您必须已经有某种嵌套环境的方法;即构造一些引用外部环境的新绑定。)

如果除了词法之外还想支持动态范围,则不需要为此传递环境;它可以隐式完成。线程局部变量可以维护动态环境或类似的东西。

(细节取决于解释器的设计;在某些设计中,总是有一些上下文对象被传递(代表解释器)。如果你去,比如说,一个带有显式堆栈的字节码虚拟机,你将需要它。)

于 2012-04-17T04:10:02.370 回答
2

我强烈建议阅读 Christian Queinnec 的书Lisp in Small Pieces,其中详细描述了许多实现 Lisp(或类似 Scheme)评估器、解释器和编译器的方法。

您需要处理与本地值不同的封闭值。

于 2012-04-15T15:05:49.307 回答