Common lisp 如何在Common Lisp[sbcl]中的变量中存储宏?

Common lisp 如何在Common Lisp[sbcl]中的变量中存储宏?,common-lisp,Common Lisp,我对宏和函数之间的区别感到困惑;鉴于第一部分的成功,下面具体说明第二部分失败的原因 (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 arg

我对宏和函数之间的区别感到困惑;鉴于第一部分的成功,下面具体说明第二部分失败的原因

(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

这是因为宏不是函数,所以不能将它们作为函数来引用(
#'foo
(function foo)
的缩写,它为symbol
foo
获取相应的函数对象),并且不能
funcall
它们


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

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

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

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

在旧的Lisp方言中,有所谓的FEXPR的概念,它类似于宏,可以在运行时调用,也可以在运行时操作源代码。此功能已为Common Lisp删除。有关这方面的背景信息,请参见Kent Pitman。

是一个标准的宏字符,它是一个分派宏字符。它应该和另一个角色一起创作。组合后面需要一个函数名或lambda表达式,并扩展为
(函数表达式)

因此,
#'foo
在读取时扩展为
(函数foo)
。如果
foo
是一个函数,将对其求值。在词法范围中,它可能是由a查找到的
foo
fB。如果没有这样的词法定义,它将尝试从符号的函数中获取全局函数定义

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

除非您要将
bar
的宏函数应用于表单,否则它可能不是您想要的。让我们考虑一下应用
的宏函数:它不会执行逻辑布尔运算,它可能会将给定的表单扩展为
if

但是,如果这是您想要的,请记住,
宏函数
还有第二个可选参数,即环境。您可以在
defmacro
或中获取环境作为参数。在后者中,通常需要这样做,以便在扩展子表单时考虑词法环境

试试这个:

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

练习:实现你自己的

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


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

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

似乎我在这个问题上的初衷可以通过:(setf(宏函数'sym1)(宏函数'bar))来实现