2

我对宏和函数之间的区别感到困惑;鉴于第 1 部分成功,具体为什么第 2 部分会在下面失败。

(defun foo () "foo")
(setf a 3) ;; sets the symbol value cell to 3
(setf a #'foo) ;; PART ONE
(funcall a) ;; returns foo

(defmacro bar () "bar")
(setf b #'bar) ;; Error the macro name bar was found as an argument to function PART TWO
4

4 回答 4

7

宏不是函数。因此,您无法从宏名称中获取函数对象。您可以应用函数,但不能应用宏。

宏需要源代码并生成新的源代码。

Common Lisp 以这样一种方式定义,即可以在编译时,运行时之前完成。在一般情况下,Common Lisp 不支持运行时宏扩展。Common Lisp 这样做是为了使代码在运行之前可以完全编译。定义 Common Lisp 的目标之一是将其定义为一种允许高效执行大型 Lisp 程序的语言。运行时代码生成仅在受控方式下有用 - 否则可能会在运行时发生一类全新的执行错误。允许一般运行时代码操作的宏机制被认为是不可取的。

在较旧的 Lisp 方言中,有所谓的FEXPR的想法,它类似于宏,可以在运行时调用,并且可以在运行时操作源代码。为 Common Lisp 删除了此功能。有关这方面的背景,请参阅Kent Pitman的 Lisp 中的特殊形式。

于 2012-11-15T12:38:21.803 回答
3

( #sharpsign)是一个标准宏字符,它是一个调度宏字符。它应该与另一个角色组成。( #'sharpsign single-quote)组合后面需要一个函数名或 lambda 表达式,并且它扩展为(function expression).

因此,#'foo在读取时扩展为(function foo). 如果foo是一个函数,function将对它进行评估。在词法范围内,它可能是foo由 afletlabels绑定的 f 。如果没有这样的词法定义,它将尝试从符号的函数中获取全局函数定义。

现在,在表示宏(function bar)时发出错误信号,无论是词法的还是全局的。但是,您可以使用来获取全局宏的宏功能。如果存在,它是两个参数的函数:表单和环境。barmacroletdefmacro(macro-function 'bar)bar

除非您要将bar's 宏功能应用于表单,否则它可能不是您想要的。让我们考虑应用 的宏功能and:它不会进行逻辑布尔运算,它可能会将给定的形式扩展为if.

但是,如果这是您想要的,请记住它macro-function还有第二个可选参数,即环境。defmacro您可能会在 a或 a中获得一个环境作为参数define-setf-expander。在后者中,通常需要它以便get-setf-expansion在扩展子表单时考虑词汇环境。

试试这个:

(funcall (macro-function 'and) '(and form1 form2 form3) nil)

练习:实现你自己的macroexpand-1macroexpand.

练习:实现 a macroexpand-all, amacroexpand递归到子表单中,识别 Common Lisp 的特殊运算符。

注意:不要走得太远macroexpand-all,它需要一个代码walker,它是特定于实现的。

于 2012-11-16T00:14:50.433 回答
1

这样做的原因是宏不是函数,因此您不能将它们作为函数引用(#'foo是 的简写(function foo),它获取符号的相应函数对象foo),您也不能funcall

部分解决方案是将宏包装在一个函数(setf b (lambda () (bar)))中,同样可以funcall&rest但是,如果您期望/&body宏中有参数,它将不起作用。在这种情况下,没有通用的方法可以实现您想要的。所以你需要重新考虑你的方法。问题是:你为什么要尝试funcall宏?也许,一个普通的功能会为你的情况做?

于 2012-11-15T09:18:36.753 回答
0

它可能有助于将宏作为“编译器插件”。现在,将编译器的一部分存储在变量中是否有意义?不(甚至在 Lisp 中也没有)。

于 2012-11-16T10:08:41.113 回答