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 的结合, 可以用来编写一个遍历列表并逐行打印每个元素的函数。