评估宏形式的参数

Wei*_* 彭巍 6 macros common-lisp

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

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

考虑一个人为的例子:

(defparameter *func-body* '((print i) (+ i 1)))
Run Code Online (Sandbox Code Playgroud)

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

(defmacro our-defun (fun args &body body)
  `(defun ,fun ,args ,@body))
Run Code Online (Sandbox Code Playgroud)

所以之后(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*))))
Run Code Online (Sandbox Code Playgroud)

之后(our-defun 1)将按2预期打印出 1 并返回。我可以想到使用 来完成这项工作eval,但eval由于它在范围界定方面的特殊性,我宁愿远离。

这导致了我一开始的问题,有没有更直接或更原生的方法来做到这一点?

附注,

甲不那么人为的例子是在函数(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))))))
Run Code Online (Sandbox Code Playgroud)

Rai*_*wig 6

这就有点糊涂了。宏不会接收未评估的参数。

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

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

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

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

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

(defun foo (i) #.`(,@*foo*))
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • @Wei Peng 彭巍:宏确实在编译时获得了源代码 - 早在评估之前。 (2认同)