10.3 反引号 (Backquote)

反引号读取宏 (read-macro)使得从模版 (templates)建构列表变得有可能。反引号广泛使用在宏定义中。一个平常的引用是键盘上的右引号 (apostrophe),然而一个反引号是一个左引号。(译注: open quote 左引号,closed quote 右引号)。它称作“反引号”是因为它看起来像是反过来的引号 (titled backwards)。

(译注: 反引号是键盘左上方数字 1 左边那个: ` ,而引号是 enter 左边那个 ')

一个反引号单独使用时,等于普通的引号:

  1. > `(a b c)
  2. (A B C)

和普通引号一样,单一个反引号保护其参数被求值。

反引号的优点是,在一个反引号表达式里,你可以使用 , (逗号)与 ,@ (comma-at)来重启求值。如果你在反引号表达式里,在某个东西前面加逗号,则它会被求值。所以我们可以使用反引号与逗号来建构列表模版:

  1. > (setf a 1 b 2)
  2. 2
  3. > `(a is ,a and b is ,b)
  4. (A IS 1 AND B IS 2)

通过使用反引号取代调用 list ,我们可以写出会产生出展开式的宏。举例来说 nil! 可以定义为:

  1. (defmacro nil! (x)
  2. `(setf ,x nil))

,@ 与逗号相似,但将(本来应该是列表的)参数扒开。将列表的元素插入模版来取代列表。

  1. > (setf lst '(a b c))
  2. (A B C)
  3. > `(lst is ,lst)
  4. (LST IS (A B C))
  5. > `(its elements are ,@lst)
  6. (ITS ELEMENTS ARE A B C)

,@ 在宏里很有用,举例来说,在用剩余参数表示代码主体的宏。假设我们想要一个 while 宏,只要初始测试表达式为真,对其主体求值:

  1. > (let ((x 0))
  2. (while (< x 10)
  3. (princ x)
  4. (incf x)))
  5. 0123456789
  6. NIL

我们可以通过使用一个剩余参数 (rest parameter) ,搜集主体的表达式列表,来定义一个这样的宏,接着使用 comma-at 来扒开这个列表放至展开式里:

  1. (defmacro while (test &rest body)
  2. `(do ()
  3. ((not ,test))
  4. ,@body))