我对宏和函数之间的区别感到困惑;特别是为什么第二部分失败,因为第一部分成功。
(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
答案 0 :(得分:7)
宏不是函数。因此,您无法从宏名称获取函数对象。您可以应用函数,但不能应用宏。
宏需要源代码并生成新的源代码。
Common Lisp的定义方式可以在运行之前在编译时完成。 Common Lisp在一般情况下不支持运行时宏扩展。 Common Lisp这样做可以在运行之前完全编译代码。定义Common Lisp的目标之一是将其定义为允许高效执行大型Lisp程序的语言。运行时代码生成仅在受控方式下有用 - 否则可能在运行时发生全新的执行错误类。一种允许一般运行时代码操作的宏机制并不被认为是可取的。
在较旧的Lisp方言中,存在所谓的 FEXPRs 的概念,它类似于宏,可以在运行时调用,并且可以将源作为运行时进行操作。 Common Lisp已删除此功能。有关此背景,请参阅Kent Pitman的Special Forms in Lisp。
答案 1 :(得分:2)
#
(sharpsign)是一个标准的宏字符,它是一个调度宏字符。它应该与另一个角色组成。 #'
(sharpsign single-quote)组合需要一个函数名或lambda表达式,然后扩展为(function expression)
。
因此,#'foo
在阅读时间扩展为(function foo)
。如果foo
是一个函数,function
将对其进行求值。在词法范围内,foo
可以(function bar)
抵消flet
or labels
。如果没有这样的词法定义,它将尝试从符号的函数中获取全局函数定义。
现在,当bar
代表一个宏时,bar
表示错误,无论是词汇macrolet
还是全局defmacro
。但是,您可以使用(macro-function 'bar)
来获取全局bar
宏的宏函数。如果它存在,它是两个参数的函数:形式和环境。
除非您要将and
的宏功能应用于表单,否则它可能不是您想要的。让我们考虑应用if
的宏函数:它不会执行逻辑布尔运算,它可能会将给定的表单扩展为macro-function
。
但是,如果这是您想要的,请记住defmacro
有第二个可选参数,即环境。您可以在(funcall (macro-function 'and) '(and form1 form2 form3) nil)
或define-setf-expander
中获取一个环境作为参数。在后者中,通常需要get-setf-expansion
考虑扩展子表单中的词汇环境。
试试这个:
macroexpand-all
练习:实施自己的macroexpand-1
and macroexpand
。
练习:实现macroexpand
,macroexpand-all
递归到子表格,识别Common Lisp的特殊操作符。
注意:{{1}}不要太过分,它需要一个代码助行器,这是特定于实现的。
答案 2 :(得分:1)
原因是宏不是函数,因此你不能将它们称为函数(#'foo
是(function foo)
的简写,它获取符号的相应函数对象{ {1}}),你不能foo
他们。
部分解决方案是将函数funcall
中的宏包装起来,同样(setf b (lambda () (bar)))
。但是,如果您希望宏中有funcall
/ &rest
个参数,它将无效。在这种情况下,没有通用的方法来实现,你想要什么。所以你需要重新思考你的方法。问题是:你为什么试图&body
一个宏?也许,一个普通的功能会为你的情况做什么?
答案 3 :(得分:0)
它可能有助于将宏作为'编译器插件'。现在,将编译器的部分存储在变量中是否有意义?不(甚至不是在Lisp中)。