【发布时间】:2011-08-05 10:26:10
【问题描述】:
在下面的when宏中:
(defmacro when (condition &rest body)
`(if ,condition (progn ,@body)))
为什么会有一个“at”@ 符号?
【问题讨论】:
标签: macros lisp common-lisp quasiquotes
在下面的when宏中:
(defmacro when (condition &rest body)
`(if ,condition (progn ,@body)))
为什么会有一个“at”@ 符号?
【问题讨论】:
标签: macros lisp common-lisp quasiquotes
在 quasiquoted 部分中插入计算值时,有两个运算符:
,
,@
逗号,在准引用的sendr中插入以下表达式的值,逗号拼接要求后面的表达式是一个列表,并且只能在一个准引用的列表中使用:效果是插入所有元素运算符出现位置的准引号列表中的表达式。
做个小实验很容易看出区别
> (let ((x '(1 2 3 4))) `(this is an example ,x of expansion))
(THIS IS AN EXAMPLE (1 2 3 4) OF EXPANSION)
> (let ((x '(1 2 3 4))) `(this is an example ,@x of expansion))
(THIS IS AN EXAMPLE 1 2 3 4 OF EXPANSION)
如您所见,,@ 的使用会将列表的元素直接放在展开式内部。没有你得到的列表放在扩展中。
将,@ 与不生成列表的表达式一起使用将在执行替换时出错:
* (defun f (x) `(here ,@x we go))
F
* (f '(1 2 3))
(HERE 1 2 3 WE GO)
* (f '99)
debugger invoked on a TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {10009F80D3}>:
The value
99
is not of type
LIST
when binding SB-IMPL::X
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.
(SB-IMPL::APPEND2 99 (WE GO)) [external]
0]
在分析准引用部分时,不在列表中使用,@ 反而会出错:
* (defun g (x) `,@x)
debugger invoked on a SB-INT:SIMPLE-READER-ERROR in thread
#<THREAD "main thread" RUNNING {10009F80D3}>:
`,@X is not a well-formed backquote expression
Stream: #<SYNONYM-STREAM :SYMBOL SB-SYS:*STDIN* {10000279E3}>
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.
(SB-IMPL::BACKQUOTE-CHARMACRO #<SYNONYM-STREAM :SYMBOL SB-SYS:*STDIN* {10000279E3}> #<unused argument>)
0]
【讨论】:
@x导致扩展,而是,@x
@ 也可以被认为是解构列表并将其附加到列表中,如Practical Common Lisp 中所述。
`(a ,@(list 1 2) c)
相当于:
(append (list 'a) (list 1 2) (list 'c))
产生:
(a 1 2 c)
【讨论】:
那个宏定义等价于
(defmacro when (condition &rest body)
(list 'if condition (cons 'progn body)))
但如果没有@,则相当于
(defmacro when (condition &rest body)
(list 'if condition (list 'progn body)))
由于body 是一个列表,这将导致它被评估为一个带括号的函数调用,例如(when t 1 2 3) 会扩展为
(if t (progn (1 2 3)))
而不是正确的
(if t (progn 1 2 3))
【讨论】: