8

在以下when宏中:

(defmacro when (condition &rest body)
  `(if ,condition (progn ,@body)))

为什么有一个“at”@标志?

4

3 回答 3

17

在 quasiquoted 部分中插入计算值时,有两个运算符:

  • “逗号”运算符,
  • “逗号拼接”运算符,@

逗号,将后面表达式的值插入到准引用的 sexpr 中,逗号拼接要求后面的表达式是一个列表,并且只能在一个准引用的列表中使用:效果是将表达式的所有元素插入到 quasi-在运算符出现的位置引用列表。

做一个小实验很容易看出区别

> (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] 

,@在分析准引用部分时,使用not inside 反而是一个错误:

* (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] 
于 2011-04-14T10:36:58.763 回答
2

@也可以考虑解构列表并将其附加到列表中,如Practical Common Lisp中所述。

`(a ,@(list 1 2) c) 

相当于:

(append (list 'a) (list 1 2) (list 'c)) 

产生:

(a 1 2 c)
于 2017-07-07T20:08:01.103 回答
0

该宏定义相当于

(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))
于 2020-12-12T16:20:20.733 回答