有选择地将评估参数传递给宏形式的最佳实践是什么?
详细说明:宏的用处在于它能够接收未评估的参数,这与函数形式的默认评估规则不同。但是,评估宏参数有一个合法的用例。
考虑一个人为的例子:
(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
.目前,我可以想到一种使用
compile
的技术和 funcall
要做到这一点,如(funcall (compile nil `(lambda () (our-defun foo (i) ,@*func-body*))))
之后
(our-defun 1)
将打印出 1 并返回 2
, 如预期。我可以想到使用 eval
进行这项工作的案例,但我宁愿远离eval
由于其在范围界定方面的特殊性。这导致我一开始的问题,有没有更直接或本地的方式来做到这一点?
PS,
函数
(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))))))
最佳答案
这有点困惑。宏不接收未评估的参数。
宏获取源代码并从中创建源代码。还要记住,Lisp 中的源代码实际上是作为数据提供的。宏创建代码,它评估一些形式而一些不评估。
宏需要在编译系统中工作。运行前。在编译期间。宏看到的只是源代码,然后它从中创建源代码。将宏视为代码转换,而不是评估参数。
It would be nice if we can force the evaluation of
*func-body*
as an argument to the macro our-defun
那不是很干净。在已编译的系统中,您需要确保
*func-body*
实际上有一个有用的绑定(bind),它可以在 解决。编译时间 .如果你有一个像
DEFUN
这样的宏,让源代码静态化是有意义的。如果你想在表单中插入一些源代码,那么在读取时这样做是有意义的:(defun foo (i) #.`(,@*foo*))
但这是我通常想要避免的代码。
two library macros
(ADD-HOOK)
and(REMOVE-HOOK)
and needs to evaluate its parameters.
为什么要
ADD-HOOK
和 REMOVE-HOOK
是宏?如果你没有真正的理由,它们就应该是函数。已经因为它们使重用变得困难。如果你想做
ADD-HOOK
和 REMOVE-HOOK
出于某种原因宏,然后 UPDATE-HOOK
通常也应该是一个宏。
关于macros - 评估宏形式的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15669675/