从s表达式创建lambda

时间:2008-10-19 02:04:55

标签: lisp lambda eval common-lisp closures

我有一个s-expression绑定到Common Lisp中的变量:

(defvar x '(+ a 2))

现在我想创建一个函数,在调用时,它会在定义它的范围内计算表达式。我试过这个:

(let ((a 4))
  (lambda () (eval x)))

(let ((a 4))
  (eval `(lambda () ,x)))

但这两个都会产生问题:EVAL会评估顶级代码,因此我无法捕获表达式中包含的变量。请注意,我不能将LET表单放在EVAL中。有没有解决方案?

编辑:那么如果没有解决EVAL问题的解决方案,还有什么办法呢?

编辑:有一个问题是我到底想要做什么。我正在编写一个编译器。我想接受一个s表达式,其中变量在定义表达式的词汇环境中关闭。将它写成宏可能确实更好。

5 个答案:

答案 0 :(得分:5)

您需要创建具有必要绑定的代码。在代码周围包装一个LET并绑定要在代码中提供的每个变量:

(defvar *x* '(+ a 2))

(let ((a 4))
  (eval `(let ((a ,a))
           ,*x*)))

答案 1 :(得分:3)

CLISP实现了一个扩展来评估词法环境中的表单。从它是一个扩展的事实来看,我怀疑你不能以符合标准的方式做到这一点。

(ext:eval-env x (ext:the-environment))

请参阅http://clisp.cons.org/impnotes.html#eval-environ

答案 2 :(得分:2)

您想要解决的实际问题是什么?最有可能的是,你试图以错误的方式解决它。词汇绑定适用于在其范围内出现词汇的内容,而不适用于从外部获取的随机内容。

也许你想要动态关闭? Common Lisp中不存在这样的事情,虽然它在一些Lisp方言中有用(就像我所理解的那样,就像Pico Lisp一样)。

请注意,可以执行以下操作,类似于:

(defvar *a*)
(defvar *x* '(+ *a* 2))  ;'

(let ((a 10))
  ;; ...
  (let ((*a* a))
    (eval *x*)))

我建议你仔细思考你是否真的想要这个。

答案 3 :(得分:0)

在Common Lisp中,您可以定义*evalhook*,这样您就可以将环境传递给(eval ...)*evalhook*与平台无关。

答案 4 :(得分:0)

可以使用COMPILE将表达式编译成函数,然后在动态设置变量的环境中使用PROGV来对已编译函数进行FUNCALL。或者,更好的是,使用COMPILE将表达式编译为接受变量的函数。

Compile接受函数定义作为列表并将其转换为函数。对于SBCL,此函数被编译为机器代码并将有效执行。

第一个选项(使用compile和progv):

(defvar *fn* (compile nil '(lambda () (+ a 2)))
(progv '(a) '(4) (funcall *fn*))
=>
6

第二个选项:

(defvar *fn* (compile nil '(lambda (a) (+ a 2))))
(funcall *fn* 4)
=>
6