为什么这段代码不起作用?
(setf x '(foo bar (baz)))
(labels
((baz () (print "baz here")))
(baz) ;works
(eval (third x))) ;fails with the below message
*** - EVAL: undefined function BAZ
我正在使用 GNU CLISP。
为什么这段代码不起作用?
(setf x '(foo bar (baz)))
(labels
((baz () (print "baz here")))
(baz) ;works
(eval (third x))) ;fails with the below message
*** - EVAL: undefined function BAZ
我正在使用 GNU CLISP。
在 Common Lisp 中,eval
在空词法环境中评估其参数,因此baz
无法找到您的词法绑定函数。
虽然 Common Lisp 标准没有提供访问词法环境并使用它调用 eval 的可移植方式,但您的实现可能具有此功能。例如,在 CLISP 中:
cs-user> (setf x '(foo bar (baz)))
(foo bar (baz))
cs-user> (labels ((baz () (print "baz here")))
(eval-env (third x) (the-environment)))
"baz here"
"baz here"
cs-user>
有关其他方法,请参阅 geocar 的答案。
检查描述EVAL
:
由于词法环境为空,因此您的baz
函数(由 定义labels
)不可访问。你会认为你可以通过放入baz
动态环境来解决这个问题(你可能想要类似(declare (special baz))
或(declare (special (function baz)))
类似的东西)但是唉:没有办法做到这一点。
(defvar baz* nil)
(defun baz (&rest args) (apply baz* args))
然后,您需要动态设置baz*
而不是使用labels
:
(setf x '(foo bar (baz)))
(let ((baz* (lambda () (print "baz here"))))
(eval (third x)))
原因只是一些关于优化的难点泄漏到规范中。基本上,每个函数调用都需要一些存根,除非编译器可以证明该函数永远不会被动态定义。这很难有效地做到,而且大多数 CL 程序员从来没有做过,所以规范编写者干脆禁止它。
正如您所看到的,与 CL 中的大多数东西一样,如果需要,您可以轻松地自己获取它。然而。鉴于大多数 CL 程序员从不这样做,您可能需要重新检查为什么要尝试以这种方式做事。