2

我有一个 s 表达式绑定到 Common Lisp 中的一个变量:

(defvar x '(+ a 2))

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

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

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

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

编辑:所以如果没有解决 EVAL 问题,还能怎么做?

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

4

5 回答 5

5

您需要创建具有必要绑定的代码。在您的代码周围包裹一个 LET 并绑定您希望在代码中可用的每个变量:

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

(let ((a 4))
  (eval `(let ((a ,a))
           ,*x*)))
于 2009-03-05T13:15:41.877 回答
3

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

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

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

于 2008-10-19T02:54:18.123 回答
2

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

也许你想要一个动态闭包?这样的事情在 Common Lisp 中不存在,尽管它在某些 Lisp 方言中存在(据我所知,如 Pico Lisp)。

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

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

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

不过,我建议你仔细考虑一下你是否真的想要这个。

于 2008-10-20T19:30:37.913 回答
0

在 Common Lisp 中,您可以定义*evalhook*它允许您将环境传递给(eval ...). *evalhook*独立于平台。

于 2008-10-20T15:08:59.937 回答
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
于 2009-03-05T19:15:02.917 回答