5.3 条件 (Conditionals)
最简单的条件式是 if
;其余的条件式都是基于 if
所构造的。第二简单的条件式是 when
,它接受一个测试表达式(test expression)与一个代码主体。若测试表达式求值返回真时,则对主体求值。所以
(when (oddp that)
(format t "Hmm, that's odd.")
(+ that 1))
等同于
(if (oddp that)
(progn
(format t "Hmm, that's odd.")
(+ that 1)))
when
的相反是 unless
;它接受相同的实参,但仅在测试表达式返回假时,才对主体求值。
所有条件式的母体 (从正反两面看) 是 cond
, cond
有两个新的优点:允许多个条件判断,与每个条件相关的代码隐含在 progn
里。 cond
预期在我们需要使用嵌套 if
的情况下使用。 举例来说,这个伪 member 函数
(defun our-member (obj lst)
(if (atom lst)
nil
(if (eql (car lst) obj)
lst
(our-member obj (cdr lst)))))
也可以定义成:
(defun our-member (obj lst)
(cond ((atom lst) nil)
((eql (car lst) obj) lst)
(t (our-member obj (cdr lst)))))
事实上,Common Lisp 实现大概会把 cond
翻译成 if
的形式。
总得来说呢, cond
接受零个或多个实参。每一个实参必须是一个具有条件式,伴随着零个或多个表达式的列表。当 cond
表达式被求值时,测试条件式依序求值,直到某个测试条件式返回真才停止。当返回真时,与其相关联的表达式会被依序求值,而最后一个返回的数值,会作为 cond
的返回值。如果符合的条件式之后没有表达式的话:
> (cond (99))
99
则会返回条件式的值。
由于 cond
子句的 t
条件永远成立,通常我们把它放在最后,作为缺省的条件式。如果没有子句符合时,则 cond
返回 nil
,但利用 nil
作为返回值是一种很差的风格 (这种问题可能发生的例子,请看 292 页)。译注: Appendix A, unexpected nil 小节。
当你想要把一个数值与一系列的常量比较时,有 case
可以用。我们可以使用 case
来定义一个函数,返回每个月份中的天数:
(defun month-length (mon)
(case mon
((jan mar may jul aug oct dec) 31)
((apr jun sept nov) 30)
(feb (if (leap-year) 29 28))
(otherwise "unknown month")))
一个 case
表达式由一个实参开始,此实参会被拿来与每个子句的键值做比较。接着是零个或多个子句,每个子句由一个或一串键值开始,跟随着零个或多个表达式。键值被视为常量;它们不会被求值。第一个参数的值被拿来与子句中的键值做比较 (使用 eql
)。如果匹配时,子句剩余的表达式会被求值,并将最后一个求值作为 case
的返回值。
缺省子句的键值可以是 t
或 otherwise
。如果没有子句符合时,或是子句只包含键值时,
> (case 99 (99))
NIL
则 case
返回 nil
。
typecase
宏与 case
相似,除了每个子句中的键值应为类型修饰符 (type specifiers),以及第一个实参与键值比较的函数使用 typep
而不是 eql
(一个 typecase
的例子在 107 页)。 译注: 6.5 小节。