我有一个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表达式,其中变量在定义表达式的词汇环境中关闭。将它写成宏可能确实更好。

有帮助吗?

解决方案

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

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

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

其他提示

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

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

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

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

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

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

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

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

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

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

可以使用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
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top