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