如何建立将在宏展开期间活动的变量绑定



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

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

但是,如果我想在函数定义期间将*test*绑定到1,那么宏展开操作时该绑定有效,并且对test的调用产生1而不是NIL,该怎么办?

仅仅在let中包装defun不起作用:

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

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

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

但是还有其他的方法吗?

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

成为Common Lisp的早期版本通过COMPILER-LET包含了这种机制,但它已经被删除了。有关更多细节,请参阅COMPILER-LET-CONFUSION问题。从词法上讲,一些效果可以使用MACROLET/SYMBOL-MACROLET来实现。从动态角度来看,很难做到这一点,如果有必要使用实际的动态绑定,我建议重新考虑这种方法。

您可以像这样引入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

用宏来控制求值时间怎么样这里是将一个已知值赋给一个已知变量,但是它可以很容易地扩展来处理更多的变量,因为我们在玩使用动态变量):

(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)。所以这不是一个好的结合模拟器"。

我认为这是因为*test*变量在let的主体内是有效的。

相关内容

  • 没有找到相关文章

最新更新