2013-03-27 57 views
4

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

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

考虑一个人为的例子:

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

假设这将是很好的是*func-body*可以作为一个宏观our-defun身体被定义为:所以(our-defun foo (i) (+ 1 i))

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

,我们可以比如(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)))))) 

回答

4

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

宏获取源代码并从中创建源代码。请记住,Lisp中的源代码实际上是作为数据提供的。该宏创建代码,它评估一些表单,有些则不是。

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

这将是很好,如果我们可以强制的*func-body*评价作为参数传递给宏我们-defun定义

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

如果您有一个像DEFUN这样的宏,让源代码变为静态是有意义的。如果要插入一些源代码转换成一种形式,那么它可以使情理之中的事情,在读时间:

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

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

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

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

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

+0

感谢您的解释。 通过“未评估的参数”,我的意思是宏绕过适用于函数参数的递归评估规则,并获取从阅读器生成的原始s-exp。 “读取时间评估”阅读器宏#的问题。在这种情况下,正如你所观察到的,它内部的表达式,即* func-body *在读取时没有值。 在这种情况下,我没有写(加钩)和(删除挂钩)。他们是由我以外的人写的。这就是为什么我需要找出将运行时值注入其参数的方法。 – 2013-03-28 00:17:28

+2

@魏鹏彭巍:宏在编译时确实得到了源代码 - 在评估之前很久。 – 2013-03-28 06:14:54

0

你是给你的宏列表的形式

(Quote (...)) 

所以你真正想要的清单是你得到的名单的CADR。