5.3 条件 (Conditionals)

最简单的条件式是 if ;其余的条件式都是基于 if 所构造的。第二简单的条件式是 when ,它接受一个测试表达式(test expression)与一个代码主体。若测试表达式求值返回真时,则对主体求值。所以

  1. (when (oddp that)
  2. (format t "Hmm, that's odd.")
  3. (+ that 1))

等同于

  1. (if (oddp that)
  2. (progn
  3. (format t "Hmm, that's odd.")
  4. (+ that 1)))

when 的相反是 unless ;它接受相同的实参,但仅在测试表达式返回假时,才对主体求值。

所有条件式的母体 (从正反两面看) 是 condcond 有两个新的优点:允许多个条件判断,与每个条件相关的代码隐含在 progn 里。 cond 预期在我们需要使用嵌套 if 的情况下使用。 举例来说,这个伪 member 函数

  1. (defun our-member (obj lst)
  2. (if (atom lst)
  3. nil
  4. (if (eql (car lst) obj)
  5. lst
  6. (our-member obj (cdr lst)))))

也可以定义成:

  1. (defun our-member (obj lst)
  2. (cond ((atom lst) nil)
  3. ((eql (car lst) obj) lst)
  4. (t (our-member obj (cdr lst)))))

事实上,Common Lisp 实现大概会把 cond 翻译成 if 的形式。

总得来说呢, cond 接受零个或多个实参。每一个实参必须是一个具有条件式,伴随着零个或多个表达式的列表。当 cond 表达式被求值时,测试条件式依序求值,直到某个测试条件式返回真才停止。当返回真时,与其相关联的表达式会被依序求值,而最后一个返回的数值,会作为 cond 的返回值。如果符合的条件式之后没有表达式的话:

  1. > (cond (99))
  2. 99

则会返回条件式的值。

由于 cond 子句的 t 条件永远成立,通常我们把它放在最后,作为缺省的条件式。如果没有子句符合时,则 cond 返回 nil ,但利用 nil 作为返回值是一种很差的风格 (这种问题可能发生的例子,请看 292 页)。译注: Appendix A, unexpected nil 小节。

当你想要把一个数值与一系列的常量比较时,有 case 可以用。我们可以使用 case 来定义一个函数,返回每个月份中的天数:

  1. (defun month-length (mon)
  2. (case mon
  3. ((jan mar may jul aug oct dec) 31)
  4. ((apr jun sept nov) 30)
  5. (feb (if (leap-year) 29 28))
  6. (otherwise "unknown month")))

一个 case 表达式由一个实参开始,此实参会被拿来与每个子句的键值做比较。接着是零个或多个子句,每个子句由一个或一串键值开始,跟随着零个或多个表达式。键值被视为常量;它们不会被求值。第一个参数的值被拿来与子句中的键值做比较 (使用 eql )。如果匹配时,子句剩余的表达式会被求值,并将最后一个求值作为 case 的返回值。

缺省子句的键值可以是 totherwise 。如果没有子句符合时,或是子句只包含键值时,

  1. > (case 99 (99))
  2. NIL

case 返回 nil

typecase 宏与 case 相似,除了每个子句中的键值应为类型修饰符 (type specifiers),以及第一个实参与键值比较的函数使用 typep 而不是 eql (一个 typecase 的例子在 107 页)。 译注: 6.5 小节。