11.1.1 while 循环与列表

控制 while 循环的常用方式是判断列表是否还有元素。 有元素则继续循环,无元素则结束循环。 这是一项重要技术,我们用简短示例说明。

判断列表是否有元素的简单方法是直接对列表求值: 若无元素,即为空列表,返回 (), 等价于 nil(假)。 反之,包含元素的列表在求值时会返回这些元素。 由于 Emacs Lisp 将非 nil 的值均视为真, 因此非空列表在 while 中会被判定为真。

例如,你可以通过下面的 setq 表达式 将变量 empty-list 设为 nil:

(setq empty-list ())

执行该表达式后,你可以像平常一样对变量 empty-list 求值: 将光标放在符号后按下 C-x C-e, 回显区会显示 nil:

empty-list

反之,如果将变量设为包含元素的列表, 求值时就会显示该列表,执行下面两个表达式即可看到:

(setq animals '(gazelle giraffe lion tiger))

animals

因此,要创建一个判断列表 animals 是否有元素的 while 循环,循环开头可以这样写:

(while animals
       ...

当 while 判断第一个参数时,变量 animals 被求值, 返回一个列表。只要列表非空,while 就认为条件为真; 当列表为空时,则认为条件为假。

为避免 while 无限循环, 需要提供一种机制让列表最终变为空。 常用方法是在 while 表达式中使用某条语句 将列表设为自身的 CDR。 每次执行 cdr,列表都会变短, 直到最后只剩下空列表。 此时 while 的条件返回假, 循环不再继续执行。

例如,绑定到 animals 的动物列表 可以通过下面的表达式设为原列表的 CDR:

(setq animals (cdr animals))

执行前面的表达式后再运行该语句, 回显区会显示 (giraffe lion tiger)。 再次执行,会显示 (lion tiger)。 继续执行,依次出现 (tiger) 和空列表(nil)。

使用 cdr 让条件最终变为假的 while 循环结构如下:

(while test-whether-list-is-empty
  body...
  set-list-to-cdr-of-list)

这种判断方式与 cdr 的结合, 可以用来编写一个遍历列表并逐行打印每个元素的函数。