11.2 条件判断

条件控制结构用于在多个备选逻辑中做出选择。Emacs Lisp 提供五种条件形式:if(其用法与其他编程语言基本一致)、whenunless(均为 if 的变体)、cond(通用化的分支语句)以及 pcasecond 的进一步通用化形式,see 模式匹配条件)。

Special Form: if condition then-form else-forms…

if 会根据 condition(条件)的求值结果,在 then-form(then 分支形式)和 else-forms(else 分支形式)之间选择执行逻辑:若 condition 求值结果为非 nil,则执行 then-form 并返回其结果;否则按文本顺序执行所有 else-forms,并返回最后一个形式的求值结果。(ifelse 部分是隐式 progn 的典型示例。See 顺序执行。)

condition 的求值结果为 nil,且未指定任何 else-forms,则 if 返回 nil

if 属于特殊形式,原因在于未被选中的分支永远不会被求值 —— 会被直接忽略。因此在以下示例中,true 不会被打印出来,因为 print 函数根本不会被调用:

(if nil
    (print 'true)
  'very-false)
⇒ very-false
Macro: when condition then-forms…

这是 if 的一个变体形式,它不含 else-forms(else 分支形式),且可以包含多个 then-forms(then 分支形式)。具体来说,

(when condition a b c)

完全等价于

(if condition (progn a b c) nil)
Macro: unless condition forms…

这是 if 的一个变体形式,它不含 then-form(then 分支形式):

(unless condition a b c)

完全等价于

(if condition nil
   a b c)
Special Form: cond clause…

cond 可在任意数量的备选逻辑中做选择。cond 中的每个 clause(分支子句)都必须是一个列表:该列表的 CAR(首部元素)为 condition(条件);若列表还有剩余元素,则这些元素为 body-forms(主体形式)。因此,一个分支子句的结构如下:

(condition body-forms...)

cond 会按照文本顺序依次尝试各个分支子句:先对每个子句的 condition(条件)进行求值。若 condition 的求值结果为非 nil,则该子句判定为「成功」;此时 cond 会执行该子句的 body-forms(主体形式),并返回最后一个 body-forms 的求值结果。后续所有剩余子句都会被忽略。

condition 的求值结果为 nil,则该子句判定为「失败」,cond 会继续处理下一个子句,对其 condition 进行求值判断。

分支子句也可以采用如下形式:

(condition)

此时,若检测到 condition(条件)的求值结果为非 nil,则该 cond 形式会返回 condition 的值。

若所有 condition 的求值结果均为 nil(即所有分支子句都判定为失败),则 cond 返回 nil

以下示例包含四个分支子句,分别用于检测 x 的值是否为数字、字符串、缓冲区和符号:

(cond ((numberp x) x)
      ((stringp x) x)
      ((bufferp x)
       (setq temporary-hack x) ; multiple body-forms
       (buffer-name x))        ; in one clause
      ((symbolp x) (symbol-value x)))

我们经常需要在前面所有分支都不匹配时,执行最后一个分支。为此,我们可以把 t 用作最后一个分支的 condition(条件),形如:(t body-forms)。形式 t 的求值结果是 t,它永远不会是 nil,因此只要 cond 执行到这个分支,它就一定会成功。例如:

(setq a 5)
(cond ((eq a 'hack) 'foo)
      (t "default"))
⇒ "default"

这个 cond 表达式在 a 的值为 hack 时返回 foo,否则返回字符串 "default"

所有条件结构都可以用 condif 来表示。因此,二者之间的选择只是风格问题。例如:

(if a b c)
≡
(cond (a b) (t c))

在使用条件判断的同时绑定变量往往会很方便。常见的场景是:你先计算出一个值,然后在该值非 nil 时对它做一些处理。直接的写法可以像下面这样,例如:

(let ((result1 (do-computation)))
  (when result1
    (let ((result2 (do-more result1)))
      (when result2
        (do-something result2)))))

由于这是一种非常常见的模式,Emacs 提供了许多宏来让这一写法更简便、更易读。上面的代码也可以改用下面这种方式来写:

(when-let* ((result1 (do-computation))
            (result2 (do-more result1)))
  (do-something result2))

围绕这种模式有许多变体,下面会对它们做简要说明。

Macro: if-let* varlist then-form else-forms...

依次对 varlist 中的每个绑定进行求值,若某个绑定的值为 nil 则停止。如果所有绑定的值都非 nil,则返回 then-form 的值;否则返回 else-forms 中最后一个形式的值。

varlist 中的每个元素都具有格式 (symbol value-form):对 value-form 求值,并将 symbol 局部绑定到该结果。绑定是顺序进行的,与 let* 类似(see 局部变量)。作为一种特殊情况:如果只关心 value-form 的判断结果,可以省略 symbol:此时会对 value-form 求值并检查是否为 nil,但不会绑定它的值。

Macro: when-let* varlist then-forms...

依次对 varlist 中的每个绑定求值,若某个绑定的值为 nil 则停止。如果所有绑定都非 nil,则返回 then-forms 中最后一个形式的值。

varlist 的格式与 if-let* 相同:varlist 中的每个元素格式为 (symbol value-form),其中对 value-form 求值,并将 symbol 局部绑定到该结果。绑定按顺序进行,与 let* 一致(see 局部变量)。作为一种特殊情况:如果只关心 value-form 的判断结果,可以省略 symbol:此时会对 value-form 求值并检查是否为 nil,但不会绑定其值。

Macro: and-let* varlist then-forms...

依次对 varlist 中的每个绑定求值,若某个绑定的值为 nil 则停止。如果所有绑定都非 nil,则返回 then-forms 中最后一个形式的值;如果没有 then-forms,则返回最后一个绑定的值。

varlist 的格式与 if-let* 相同:varlist 中的每个元素格式为 (symbol value-form),其中对 value-form 求值,并将 symbol 局部绑定到该结果。绑定按顺序进行,与 let* 一致(see 局部变量)。作为一种特殊情况:如果只关心 value-form 的判断结果,可以省略 symbol:此时会对 value-form 求值并检查是否为 nil,但不会绑定其值。

部分 Lisp 程序员遵循这样的惯例:andand-let* 用于关注返回值的形式求值,而 whenwhen-let* 用于关注副作用、忽略返回值的形式求值。

Emacs 还提供了类似的宏,用于循环执行直到某个绑定求值为 nil

Macro: while-let spec then-forms...

依次对 spec 中的每个绑定求值,若某个绑定的值为 nil 则停止。如果所有绑定都非 nil,则执行 then-forms,然后重复循环。注意:循环重复时,spec 中的 value-form 会被重新求值,绑定也会重新建立。

varlist 的格式与 if-let* 相同:varlist 中的每个元素格式为 (symbol value-form),其中对 value-form 求值,并将 symbol 局部绑定到该结果。绑定按顺序进行,与 let* 一致(see 局部变量)。作为一种特殊情况:如果只关心 value-form 的判断结果,可以省略 symbol:此时会对 value-form 求值并检查是否为 nil,但不会绑定其值。

while-let 的返回值永远是 nil


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike