10.2 宏 (Macros)

写出能写程序的程序的最普遍方法是通过定义宏。是通过转换 (transformation)而实现的操作符。你通过说明你一个调用应该要翻译成什么,来定义一个宏。这个翻译称为宏展开(macro-expansion),宏展开由编译器自动完成。所以宏所产生的代码,会变成程序的一个部分,就像你自己输入的程序一样。

宏通常通过调用 defmacro 来定义。一个 defmacro 看起来很像 defun 。但是与其定义一个函数调用应该产生的值,它定义了该怎么翻译出一个函数调用。举例来说,一个将其参数设为 nil 的宏可以定义成如下:

  1. (defmacro nil! (x)
  2. (list 'setf x nil))

这定义了一个新的操作符,称为 nil! ,它接受一个参数。一个这样形式 (nil! a) 的调用,会在求值或编译前,被翻译成 (setf a nil) 。所以如果我们输入 (nil! x) 至顶层,

  1. > (nil! x)
  2. NIL
  3. > x
  4. NIL

完全等同于输入表达式 (setf x nil)

要测试一个函数,我们调用它,但要测试一个宏,我们看它的展开式 (expansion)。

函数 macroexpand-1 接受一个宏调用,并产生它的展开式:

  1. > (macroexpand-1 '(nil! x))
  2. (SETF X NIL)
  3. T

一个宏调用可以展开成另一个宏调用。当编译器(或顶层)遇到一个宏调用时,它持续展开它,直到不可展开为止。

理解宏的秘密是理解它们是如何被实现的。在台面底下,它们只是转换成表达式的函数。举例来说,如果你传入这个形式 (nil! a) 的表达式给这个函数

  1. (lambda (expr)
  2. (apply #'(lambda (x) (list 'setf x nil))
  3. (cdr expr)))

它会返回 (setf a nil) 。当你使用 defmacro ,你定义一个类似这样的函数。 macroexpand-1 全部所做的事情是,当它看到一个表达式的 car 是宏时,将表达式传给对应的函数。