Macros 公共Lisp宏宏宏扩展时间参数类型
在下面的函数中,我用mac-1替换宏,它只返回它工作的参数值,但是mac-2抛出一个类型错误。有人能解释一下发生了什么并导致了这个错误吗?为什么从fun-2传递给mac-2的x是一个文字符号Macros 公共Lisp宏宏宏扩展时间参数类型,macros,lisp,common-lisp,Macros,Lisp,Common Lisp,在下面的函数中,我用mac-1替换宏,它只返回它工作的参数值,但是mac-2抛出一个类型错误。有人能解释一下发生了什么并导致了这个错误吗?为什么从fun-2传递给mac-2的x是一个文字符号x,而不是一个整数 (defmacro mac-1 (x) x) (defun fun-1 (x) (mac-1 x)) (fun-1 3) ; => 3 (defmacro mac-2 (x) (+ x 3)) (defun fun-2 (x) (mac-2 x)) (f
x
,而不是一个整数
(defmacro mac-1 (x) x)
(defun fun-1 (x) (mac-1 x))
(fun-1 3) ; => 3
(defmacro mac-2 (x) (+ x 3))
(defun fun-2 (x) (mac-2 x))
(fun-2 3)
执行带错误编译的表单。
表格:
(MAC-2 X)
编译时错误:
在(MAC-2 X)的宏扩展期间。使用中断信号进行拦截
价值
X
他不是那种人
数
绑定SB-KERNEL::X时
(defmacro mac-1(x)x)
工作,因为当定义fun-1
时,它将x
扩展到x
。基本上这是一个角落。它适用于任何表达式,因为它在编译时不进行计算
(defmacro mac-2(x)(+x3))
仅在语法x
为文本时工作。例如,(mac-23)
被转换为6
,但在你的函数中你给它x
。请记住宏转换语法,以便执行(+'x 3)
(绑定x
具有fun-2
中给定的值,即符号x
)。发生此扩展时,变量x
甚至不存在,因此它甚至没有值
如果您希望表达式的结果变成一个数字,则可以使mac-2
在x
存在时进行计算。例如
(defmacro mac-2 (x)
`(+ ,x 3))
创建fun2
时,它将展开宏,不会立即尝试进行计算,但函数将存储为:
(defun fun-2 (x) (+ x 3))
然后,当您调用fun-2
x
时,将存在(+x3)
。在创建函数时执行(+x 3)
,因为此时宏获取x
,而x
尚不存在 重命名该对象:
(defmacro mac-2 (source-code-expression)
(declare (type (or number list symbol string) ; actually T,
; since all types are allowed
source-code-expression))
(+ source-code-expression 3)) ; here we have a type problem
这只适用于数字。但是:例如,一个符号不能加3
记住:宏接受任意表达式并创建新表达式。在宏扩展时,应该看到源代码,而不是值
表示从
fun-2
传递到mac-2
的x
这里是源代码(数据)吗?一旦计算了fun-2
,宏就会用该数据展开(符号x
),并将展开放入fun-2
?一步一步地,我开始明白我还不懂宏
几乎:也可以在计算函数之前展开宏。例如,在编译函数时,宏将展开-->显然,如果宏需要运行时值,这是不可能的
宏取代了更多的动态机制,正是因为它们能够编译高效的代码,而在运行时不需要宏扩展
函数将x
传递给宏的心智模型实际上没有多大帮助
第一个基本思想是:宏将把宏形式转换成一些新的表达式——在编译期间在编译实现中,在计算期间在解释版本中
是宏的名称mac-2
是一种宏形式,因为它是一种复合形式,(mac-2 x)
是宏的名称mac-2
您能解释一下宏中的类型声明是用来做什么的吗 只是为了弄清楚作为参数类型,您可以期望什么。 不要在代码中使用它,而是使用
检查类型
(见下文)
通常在可移植代码中,当宏希望某个参数具有某种类型或形状时,可能需要添加check type
调用来明确这一点。假设我们要编写一个宏来定义一个行星,我们需要一个名称,它应该是一个符号或字符串:
(defmacro defplanet (name coordinates)
(check-type name (or string symbol)
"The name of the planet must be a symbol or a string.")
`(intern-planet ,name (check-coordinates ,coordinates)))
上面将在宏展开时检查“name”值的类型。了解Lisp中宏的关键是它们指定源代码到源代码的转换:宏获取一位Lisp源代码并返回另一位Lisp源代码,其中表示源代码(在公共Lisp和其他相关Lisp中)作为s表达式。这种扩展发生在代码被评估之前——从概念上讲,它发生在为评估准备代码的过程中,通常是在编译代码的过程中 特别是,像
(defmacro foo (x)
...)
定义一个非常普通的Lisp函数,其参数是源代码块(一个表单),并且需要返回另一个源代码块
使用defmacro
定义宏时,函数得到的形式通常对您不可见,因为defmacro
会根据您提供的参数列表将其分开,并仅显示您关心的部分。但是在宏参数列表中有一个特殊的选项,&whole
,它可以让您看到整个表单
因此,我们可以定义宏的一个版本,该版本显示如何调用宏,以及源代码块是什么:
(defmacro mac-1 (&whole form x)
(format *debug-io* "~&mac-1 called with ~S~%" form)
x)
这与您的mac-1
相同,但它将在宏扩展时打印调用它的表单
现在我们可以定义一个函数,它使用:
(defun fun-1 (x)
(mac-1 x))
当调用与mac-1对应的函数时,调用次数取决于实现。在仅编译器实现中,当对该表单求值时,它可能至少被调用一次。在带有解释器的实现中,它可能稍后被调用。在我使用的实现(LispWorks)中,调用它的简单方法是显式编译
(defun fun-1 (x)
(mac-1 x))
> (defun fun-1 (x) (mac-1 x))
fun-1
> (compile 'fun-1)
mac-1 called with (mac-1 x)
fun-1
nil
nil
(defmacro mac-2 (&whole form x)
;; broken
(format *debug-io* "~&mac-2 called with ~S~%" form)
(+ x 3))
> (defun fun-2 (x) (mac-2 x))
fun-2
> (compile *)
mac-2 called with (mac-2 x)
Error: In + of (x 3) arguments should be of type number.
1 (continue) Return a value to use.
2 Supply a new first argument.
3 (abort) Return to top loop level 0.
(defmacro mac-2 (&whole form x)
(format *debug-io* "~&mac-2 called with ~S~%" form)
(let ((result (list '+ x 3))) ;aka `(+ ,x 3)
(format *debug-io* "~&mac-2 -> ~S~%" result)
result))
> (defun fun-3 (x) (mac-2 (sin x)))
fun-3
> (compile *)
mac-2 called with (mac-2 (sin x))
mac-2 -> (+ (sin x) 3)
fun-3
nil
nil
(defun fun-4 (x)
(mac-2 (mac-2 (sin x))))