Stu*_*ent 0 lisp macros common-lisp
在下面的函数中,我用 mac-1 替换了宏,它只返回它工作的参数的值,但 mac-2 抛出一个类型错误。有人可以解释会发生什么并导致此错误吗?为什么 x 从 fun-2 传递给 mac-2 是文字符号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)
Run Code Online (Sandbox Code Playgroud)
执行编译有错误的表单。形式:(MAC-2 X) 编译时错误:在 (MAC-2 X) 的宏扩展期间。使用BREAK-ON-SIGNALS进行拦截。
绑定 SB-KERNEL::X 时,值 X 不是 NUMBER 类型
重命名事物:
(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
Run Code Online (Sandbox Code Playgroud)
这仅适用于数字。但是:例如,不能将 3 添加到符号中。
请记住:宏接受任意表达式并创建新表达式。在宏扩展时,应该看到源代码,而不是值。
手段
x从传递fun-2到mac-2这里的源代码(数据)?并且在评估后x立即使用此数据(符号)扩展宏fun-2,并将扩展放入fun-2? 一步一步我开始明白我还不了解宏!
几乎:宏也可以在评估函数之前展开。例如,当您编译一个函数时,宏会被扩展 --> 显然这是不可能的,如果宏需要运行时值。
宏取代了更多的动态机制,正是因为它们能够编译为高效的代码,在运行时不需要宏扩展。
传递x给宏的函数的心智模型并没有那么有用。
更像是第一个基本思想:宏形式将被宏转换为一些新的表达式 - 在编译期间的编译实现和评估期间的解释版本中。
mac-2 是宏的名称(mac-2 x)是一个宏形式,因为它是一个复合形式并且mac-2是一个宏的名称。通常,所有将被评估的表达式都称为形式:函数形式、宏形式、特殊形式、lambda 形式、符号和自评估对象。
你能解释一下你的宏中的类型声明是做什么的吗?
只是为了明确您可以期望作为参数类型的内容。不要在代码中使用它,check-type而是使用它(见下文)。
(let ((x 100))
(mac-2 1) ; the macro sees a number
(mac-2 x) ; the macro sees a symbol ! Not a number!
(mac-2 "x") ; the macro sees a string
(mac-2 (+ x 20)) ; the macro sees a list ! Not a number!
; and so on for other data objects
)
Run Code Online (Sandbox Code Playgroud)
通常在可移植代码中,当您的宏期望某个参数具有某种类型或形状时,您可能希望添加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)))
Run Code Online (Sandbox Code Playgroud)
以上将在宏扩展时检查'name'值的类型。
(defmacro mac-1 (x) x)有效,因为 fun-1定义时它会扩展x到x. 基本上它是一个noop。它适用于任何表达式,因为它在编译时不进行计算。
(defmacro mac-2 (x) (+ x 3))仅当语法x是文字时才有效。例如。(mac-2 3)被转换为6,但在你的函数中你给了它x。记住宏转换语法,所以你正在做(+ 'x 3)(绑定x具有给出的值,fun-2其中是符号x)。x当这种扩展发生时,该变量甚至不存在,因此它甚至没有值。
如果您希望表达式的结果成为数字,则可以mac-2在x存在时进行计算。例如。
(defmacro mac-2 (x)
`(+ ,x 3))
Run Code Online (Sandbox Code Playgroud)
当fun2创建将扩展宏,它不会尝试做计算的时候了,但功能被存储为:
(defun fun-2 (x) (+ x 3))
Run Code Online (Sandbox Code Playgroud)
当你然后调用fun-2 x将存在并且(+ x 3)有意义。在做(+ x 3)的时候,你创造功能没有因为当时的宏观得到x,同时x还不存在。