6

有选择地将评估参数传递给宏形式的最佳实践是什么?

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

考虑一个人为的例子:

(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可以考虑使用.evaleval

这导致我一开始的问题,有没有更直接或本地的方式来做到这一点?

PS,

一个不太人为的例子是在 function(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))))))
4

2 回答 2

6

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

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

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

如果我们可以强制将评估*func-body*作为宏 our-defun 的参数,那就太好了

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

如果您有一个类似 的宏DEFUN,则将源代码设为静态是有意义的。如果你想在表单中插入一些源代码,那么在读取时这样做是有意义的:

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

但这是我通常想要避免的代码。

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

为什么应该ADD-HOOKREMOVE-HOOK是宏?如果你没有真正的理由,它们就应该是函数。已经因为它们使重用变得困难。

如果您出于某种原因想要制作ADD-HOOK和宏,那么通常也应该是宏。REMOVE-HOOKUPDATE-HOOK

于 2013-03-27T22:56:37.650 回答
0

您提供给宏的列表具有以下形式

(Quote (...))

所以你真正想要的列表是你得到的列表的 CADR。

于 2013-04-17T12:37:32.850 回答