我目前正在研究用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中免费提供。)
答案 0 :(得分:4)
此类工作正常,但它不存储环境值以启用闭包。有人知道怎么做吗?在哪里做?在构造函数或eval方法中?
是的,该课程应该存储环境。一般来说,是一个成员变量。 :)
它应该在构造函数中,因为环境在构造lambda时绑定,而不是在eval时间。
在eval时间,原始环境不可用:新环境是。
如果您的方言纯粹是词法范围,那么您的lambda不需要环境参数。记住,什么是lambda?这是一个功能。 表单的评估需要一个环境。 函数的评估没有;函数的求值是一个函数调用,只需要参数。环境不会传递到功能中;函数体在具有自己的私有环境的封装空间中进行评估。 (在lambda上存在eval
函数甚至看起来是错误的;您希望将其命名为call
或类似的东西。解释的lambda 使用评估程序的服务;但它不是一个。)
在lambda中,动作将是评估lambda体的形式。这些将使用存储的环境(不是传入的任何内容)。
您必须建立一个lambda参数绑定到参数值的环境。这嵌套在捕获的环境中。 (即,一个名为x
的参数会影响一个名为x
的捕获变量。
(你必须已经有一些嵌套环境的方法;即构建一些引用外部环境的新绑定。)
如果你想支持除词法之外的动态范围,那么就不需要为此传递环境;它可以隐含地完成。线程局部变量可以维护动态环境或类似的东西。
(细节取决于解释器设计;在某些设计中,总会有一些上下文对象传递(代表解释器)。如果你去一个具有显式堆栈的字节码虚拟机,你将需要。)
答案 1 :(得分:2)
我强烈建议阅读Christian Queinnec的书Lisp in Small Pieces,其中详细描述了实现Lisp(或类似Scheme)评估器,解释器和编译器的许多方法。
您需要处理来自本地值的不同闭合值。