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

时间:2012-11-15 08:16:03

标签: 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 argument to function PART TWO

4 个答案:

答案 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

练习:实现macroexpandmacroexpand-all递归到子表格,识别Common Lisp的特殊操作符。

注意:{{1}}不要太过分,它需要一个代码助行器,这是特定于实现的。

答案 2 :(得分:1)

原因是宏不是函数,因此你不能将它们称为函数(#'foo(function foo)的简写,它获取符号的相应函数对象{ {1}}),你不能foo他们。

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

答案 3 :(得分:0)

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