Macros 公共Lisp宏宏宏扩展时间参数类型

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

在下面的函数中,我用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))
(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
    是宏的名称
通常,所有要计算的表达式都称为形式:函数形式、宏形式、特殊形式、lambda形式、符号和自评估对象


您能解释一下宏中的类型声明是用来做什么的吗

只是为了弄清楚作为参数类型,您可以期望什么。 不要在代码中使用它,而是使用
检查类型
(见下文)

通常在可移植代码中,当宏希望某个参数具有某种类型或形状时,可能需要添加
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))))