评估宏表单的参数

时间:2013-03-27 21:16:09

标签: macros common-lisp

有选择地将已评估的参数传递给宏表单的最佳做法是什么?

详细说明:宏的有用性在于它能够接收未评估的参数,这与函数表单的默认评估规则不同。但是,有一个合理的用例来评估宏参数。

考虑一个人为的例子:

(defparameter *func-body* '((print i) (+ i 1)))

假设*func-body*可以作为宏our-defun的主体定义为:

(defmacro our-defun (fun args &body body)
  `(defun ,fun ,args ,@body))

所以在(our-defun foo (i) (+ 1 i))之后,我们可以说(foo 1)来获取2。但是,如果我们使用(our-defun foo (i) *func-body*),则(foo 1)的结果将为((PRINT I) (+ I 1))(即*func-body*的值)。如果我们可以强制评估*func-body*作为宏our-defun的参数,那就太好了。

目前,我可以考虑使用compilefuncall来执行此操作的技巧,如

(funcall (compile nil `(lambda () (our-defun foo (i) ,@*func-body*))))

之后(our-defun 1)将打印出1并按预期返回2。我可以考虑使用eval进行此项工作的情况,但由于其在范围界定方面的特殊性,我宁愿远离eval

这引发了我的问题,是否有更直接或本土的方式来做到这一点?

P.S。,

一个不那么人为的例子是函数(UPDATE-HOOK),它使用两个库宏(ADD-HOOK)(REMOVE-HOOK),需要评估它的参数。这里使用了上面的(funcall (compile nil `(lambda () ...)))技术。

(defun update-hook (hook hook-name &optional code)
  (funcall (compile nil `(lambda () (remove-hook ,hook ',hook-name))))
  (unless (null code)
    (compile hook-name `(lambda () ,@code))
    (funcall (compile nil `(lambda () (add-hook ,hook ',hook-name))))))

2 个答案:

答案 0 :(得分:4)

这有点困惑。宏不会收到未评估的参数。

宏获取源代码并从中创建源代码。还要记住,Lisp中的源代码实际上是作为数据提供的。宏创建代码,评估某些表单,而不是。

宏需要在编译系统中工作。在运行之前。在编译期间。所有宏看到的都是源代码,然后它从中创建源代码。将宏视为代码转换,而不是关于是否评估参数。

  

如果我们可以强制评估*func-body*作为宏我们定义的参数

,那就太好了

那不是很干净。在已编译的系统中,您需要确保*func-body*实际上具有有用的绑定,并且可以在 COMPILE TIME 时解析。

如果您有像DEFUN这样的宏,那么让源代码保持静态是有意义的。如果你想在表单中插入一些源代码,那么在读取时这样做是有意义的:

(defun foo (i) #.`(,@*foo*))

但这是我通常希望避免的代码。

  

两个库宏(ADD-HOOK)(REMOVE-HOOK),需要评估其参数。

为什么ADD-HOOKREMOVE-HOOK应该是宏?如果你没有真正的理由,他们应该只是功能。因为它们使重复使用变得困难。

如果您出于某种原因想制作ADD-HOOKREMOVE-HOOK个宏,那么UPDATE-HOOK通常也应该是一个宏。

答案 1 :(得分:0)

您为宏提供的列表的格式为

(Quote (...))

因此,您实际需要的列表是您获得的列表的CADR。

相关问题