如何建立一个在宏扩展时间内处于活动状态的变量绑定?

时间:2012-01-04 10:50:38

标签: macros common-lisp special-variables

让我们定义一个函数,它的主体包含宏,它将在某个未指定的时间展开,并在此过程中使用全局动态值*test*

> (defvar *test* nil)
> (defmacro body ()
    `(print ,*test*))
> (defun test ()
    (body))
> (test)
NIL

但是,如果我想在函数定义期间将*test*绑定到1,以便宏扩展使用此绑定生效并且调用test生成{{} 1}}而不是1

仅在NIL中包裹defun不起作用:

let

可能与Hyperspec中的这一行有关:

  

defun不需要执行任何编译时副作用

但还有其他方法吗?

4 个答案:

答案 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)。所以这不是一个好的约束力 模拟器”。