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)
的缩写,它为symbolfoo
获取相应的函数对象),并且不能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))来实现