在下面的函数中,我用mac-1替换了宏,它只返回它工作的参数的值,但是mac-2抛出一个类型错误。有人能解释一下发生了什么并导致这个错误吗?为什么x是从x传递给mac-2的文字符号,而不是整数呢?
(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-2X)编译时错误:宏扩展期间(MAC-2X).用信号中断来拦截。 当绑定SB-核::x时,值X不是类型数。
发布于 2020-01-02 10:33:25
重命名这个东西:
(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添加到符号中。
记住:宏接受任意表达式并创建新表达式。在宏展开时,我们应该看到源代码,而不是值。
意思是
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
) 通常在可移植代码中,当宏希望某个参数具有某种类型或形状时,可能需要添加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”值的类型。
发布于 2020-01-02 09:56:43
(defmacro mac-1 (x) x)之所以有效,是因为当定义fun-1时,它将x扩展为x。从根本上说,这是个小头。它适用于任何表达式,因为它在编译时不进行计算。
只有当语法(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))创建fun2时,它将展开宏,不会立即尝试进行计算,但函数存储为:
(defun fun-2 (x) (+ x 3))然后调用fun-2时,x就会存在,(+ x 3)就有意义了。在创建函数时执行(+ x 3)不会,因为那时宏获得x,而x还不存在。
发布于 2020-01-02 11:21:28
对于Lisp中的宏,需要了解的关键是它们将源代码指定为源代码转换:宏获取一些Lisp源代码,并返回另一部分Lisp源代码,其中源代码被表示为s-表达式(在公共Lisp和其他相关的Lisps中)。这种扩展发生在代码被评估之前--从概念上讲,它是准备代码进行评估的过程的一部分,通常是编译代码过程的一部分。
特别是,像这样的表达式
(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))
fun-1
> (compile 'fun-1)
mac-1 called with (mac-1 x)
fun-1
nil
nil这里的第一个输出是mac-1报告它的参数。以及它的论点..。是源代码:这正是您在函数定义中可以看到的。
特别是,当调用宏函数时,x不是3,而是x:它只是一些源代码,宏的任务是返回另一段源代码,在本例中,这又是x。
因此,现在我们可以用同样的方式重写mac-2,您可以看到为什么它不能工作:
(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.好的,现在应该清楚为什么mac-2不能工作了:它是用一些源代码(这里是符号x)调用的,它试图将3添加到这段源代码中,除非这部分源代码恰好是字面的3,否则它就不能工作,在本例中它不是。源代码不是数字。
嗯,mac-2在生活中的目的是获取一些源代码,然后再计算一段代码。特别是,它可以计算一些源代码,在计算时会将3添加到表达式中。下面是一个这样的版本,同时打印它将要返回的内容。
(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))))如果您能够计算出发生宏扩展时会打印什么,以及为什么您可能很好地掌握了Lisp中的宏。
请注意,CL人员:我忽略了上面的环境,因为他们在这个级别上并不重要!
https://stackoverflow.com/questions/59560702
复制相似问题