让我们定义一个函数,它的主体包含宏,它将在某个未指定的时间展开,并在此过程中使用全局动态值*test*
。
> (defvar *test* nil)
> (defmacro body ()
`(print ,*test*))
> (defun test ()
(body))
> (test)
NIL
但是,如果我想在函数定义期间将*test*
绑定到1
,以便宏扩展使用此绑定生效并且调用test
生成{{} 1}}而不是1
。
仅在NIL
中包裹defun
不起作用:
let
可能与Hyperspec中的这一行有关:
defun不需要执行任何编译时副作用
但还有其他方法吗?
答案 0 :(得分:2)
正如您自己写的那样,宏在未指定的时间扩展。在我的SBCL中,在评估整个表单之前扩展宏,这意味着在LET绑定生效之前。对于某些解释器,在绑定到期后,在执行函数时可能会扩展宏。
Common Lisp的早期版本包括通过COMPILER-LET的这种机制,但它已被删除。有关详细信息,请参阅COMPILER-LET-CONFUSION issue。从词汇上讲,使用MACROLET / SYMBOL-MACROLET可以实现一些效果。如果使用实际的动态绑定似乎是必要的,那么动态地很难使这项工作变得很明智,我建议重新考虑这种方法。
答案 1 :(得分:0)
我认为这是因为*test*
变量在let
的正文中有效。
答案 2 :(得分:0)
您可以像这样介绍let
:
(defvar *test* nil)
(defmacro foo ()
(let ((test (gensym)))
`(let ((,test *test*))
(print ,test))))
(defun test-foo ()
(foo))
(test-foo) => print and returns NIL
(let ((*test* 1))
(test-foo)) => print and returns 1
答案 3 :(得分:0)
如何通过使用宏来控制评估时间 let(这里是为已知变量赋值,但它 因为我们正在玩,所以可以轻松扩展以处理更多变量 动态变量):
(defmacro letter (&body body)
(let ((old-test *test*))
(set '*test* 1)
`(progn
,@body
(set '*test* ,old-test))))
定义test
:
(letter (defun test () (body)))
使用test
:
CL-USER> (test)
1
1
这似乎与SBCL预期的一样,需要先睡个好觉 尝试其他实现。
嗯,宏扩展显然letter
正常工作
仅在宏扩展和评估时。简单地说,宏观扩张不会
将*test*
恢复为旧值(doh)。所以这不是一个好的约束力
模拟器”。