2

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

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

但是,如果我想绑定*test*到,比如说,1在函数定义期间,以便宏扩展在此绑定有效的情况下运行,并且调用test产生1而不是NIL.

只是包装defun不起作用let

> (let ((*test* 1))
    (defun test ()
      (body)))
> (test)
NIL

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

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

但是还有其他方法可以做到这一点吗?

4

4 回答 4

3

正如您自己编写的那样,宏会在未指定的时间展开。在我的 SBCL 中,宏在评估整个表单之前展开,这意味着在 LET 绑定生效之前。对于某些解释器,宏可能会在绑定过期后执行函数时展开。

成为 Common Lisp 的早期版本通过 COMPILER-LET 包含这种机制,但它已被删除。有关详细信息,请参阅COMPILER-LET-CONFUSION 问题。词汇上的一些效果可以使用 MACROLET/SYMBOL-MACROLET 来实现。动态地,很难使这项工作正常进行,如果似乎有必要使用实际的动态绑定,我建议重新考虑该方法。

于 2012-01-04T16:16:48.433 回答
1

你可以这样介绍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
于 2012-01-04T17:02:50.857 回答
1

通过使用宏而不是 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)。所以它不是一个好的“绑定模拟器”。

于 2012-01-04T21:08:53.890 回答
0

我认为这是因为*test*变量在let.

于 2012-01-04T14:21:45.163 回答