列表是 Lisp 语言的核心,因此有许多函数用于构建列表。cons 是最基础的列表构建函数;但值得注意的是,在 Emacs 源代码中,list 的使用次数比 cons 更多。
该函数是构建新列表结构最基础的函数。它会创建一个新的 cons 单元,将 object1 设为 CAR,object2 设为 CDR,然后返回这个新的 cons 单元。参数 object1 和 object2 可以是任意 Lisp 对象,但 object2 通常是一个列表。
(cons 1 '(2))
⇒ (1 2)
(cons 1 '())
⇒ (1)
(cons 1 2)
⇒ (1 . 2)
cons 常用来向列表头部添加单个元素。这一操作被称为 consing the element onto the list将元素 cons 到列表上6。
例如:
(setq list (cons newelt list))
注意:本例中名为 list 的变量与下文描述的 list 函数之间无冲突 —— 任何符号都可同时作为变量和函数名使用。
该函数创建一个以 objects 为元素的列表,生成的列表始终以 nil 结尾。若未传入任何 objects,则返回空列表。
(list 1 2 3 4 5)
⇒ (1 2 3 4 5)
(list 1 2 '(3 4 5) 'foo)
⇒ (1 2 (3 4 5) foo)
(list)
⇒ nil
该函数创建一个长度为 length 的列表,其中每个元素都是 object。可将 make-list 与 make-string 对比(see 创建字符串)。
(make-list 3 'pigs)
⇒ (pigs pigs pigs)
(make-list 0 'pigs)
⇒ nil
(setq l (make-list 3 '(a b)))
⇒ ((a b) (a b) (a b))
(eq (car l) (cadr l))
⇒ t
该函数返回一个包含所有 sequences 元素的列表。sequences 可以是列表、向量、布尔向量或字符串,但最后一个参数通常应为列表。除最后一个参数外,其余所有参数都会被复制,因此不会修改任何传入的参数。(若需无复制地拼接列表,可参见 “重排列表的函数” 中的 nconc。)
更普通地,append 的最后一个参数可以是任意 Lisp 对象。该参数不会被复制或转换,而是成为新列表最后一个 cons 单元的 CDR。若最后一个参数本身是列表,则其元素会成为结果列表的元素;若最后一个参数不是列表,结果会是一个点对列表(因为其最终 CDR 不符合规范列表要求的 nil,see 列表与 Cons 单元)。
以下是 append 的使用示例:
(setq trees '(pine oak))
⇒ (pine oak)
(setq more-trees (append '(maple birch) trees))
⇒ (maple birch pine oak)
trees
⇒ (pine oak)
more-trees
⇒ (maple birch pine oak)
(eq trees (cdr (cdr more-trees)))
⇒ t
可通过框图理解 append 的工作原理。变量 trees 指向列表 (pine oak),变量 more-trees 指向列表 (maple birch pine oak)。但 trees 仍指向原始列表:
more-trees trees
| |
| --- --- --- --- -> --- --- --- ---
--> | | |--> | | |--> | | |--> | | |--> nil
--- --- --- --- --- --- --- ---
| | | |
| | | |
--> maple -->birch --> pine --> oak
空序列不会对 append 的返回值产生任何影响。因此,若最后一个参数为 nil,会强制复制前一个参数:
trees
⇒ (pine oak)
(setq wood (append trees nil))
⇒ (pine oak)
wood
⇒ (pine oak)
(eq wood trees)
⇒ nil
在 copy-sequence 函数被发明前,这曾是复制列表的常用方式。See 序列、数组与向量。
以下示例展示向量和字符串作为 append 参数的用法:
(append [a b] "cd" nil)
⇒ (a b 99 100)
将字符串转换为字符列表的方法:
(append "abcd" nil)
⇒ (97 98 99 100)
string-to-list 函数是上述操作的便捷简写。
借助 apply(see 调用函数),我们可以将一个 “列表的列表” 中的所有列表 append拼接 起来:
(apply 'append '((a b c) nil (x y z) nil))
⇒ (a b c x y z)
若未传入 sequences,返回 nil:
(append)
⇒ nil
以下示例展示最后一个参数非列表的情况:
(append '(x y) 'z)
⇒ (x y . z)
(append '(x y) [z])
⇒ (x y . [z])
第二个示例表明:若最后一个参数是序列但非列表,其元素不会成为结果列表的元素。而是像其他非列表参数一样,直接作为最终 CDR。
例外情况:若除最后一个参数外其余均为 nil,且最后一个参数非列表,则返回值为该最后一个参数本身(即此时返回值不是列表):
(append nil nil "abcd")
⇒ "abcd"
该函数返回 tree 的副本。若 tree 是 cons 单元,则创建一个新的 cons 单元(CAR 和 CDR 与原单元相同),再递归地以同样方式复制其 CAR 和 CDR。
默认情况下,若 tree 不是 cons 单元,copy-tree 直接返回 tree;但若 vectors-and-records 为非-nil,则同时复制向量和记录(并递归处理其元素)。注意 tree 参数不能包含循环引用。
该函数返回 tree 的 “flattened扁平化” 副本,即一个包含以 tree 为根的 cons 单元树中所有非-nil 终端节点(叶子节点)的列表,且叶子节点顺序与原树 tree 中一致。
(flatten-tree '(1 (2 . 3) nil (4 5 (6)) 7))
⇒(1 2 3 4 5 6 7)
该函数将 object 转换为列表返回。若 object 已是列表,则直接返回;否则返回一个包含 object 的单元素列表。
此函数常用于处理 “可能是列表、也可能不是列表” 的变量,例如:
(dolist (elem (ensure-list foo)) (princ elem))
该函数返回一个数字列表,起始值为 from,步长为 separation,终止于 to 或 to 之前。separation 可正可负,默认值为 1。
若 to 为 nil 或与 from 数值相等,返回单元素列表 (from);
若 separation 为正但 to 小于 from,或 separation 为负但 to 大于 from,返回 nil(因参数指定了空序列);
若 separation 为 0,且 to 非-nil 且与 from 数值不等,number-sequence 则触发错误(因参数指定了无限序列)。
所有参数均为数字。浮点参数需注意:浮点运算存在精度问题。例如,不同机器上 (number-sequence 0.4 0.6 0.2) 可能返回单元素列表 (0.4),而 (number-sequence 0.4 0.8 0.2) 可能返回三个元素的列表。列表的第 n 个元素通过精确公式 (+ from (* n separation)) 计算。因此若需确保 to 被包含在列表中,可将符合该精确公式的表达式作为 to 的值传入;或者,也可将 to 替换为一个略大的值(若步长 separation 为负数,则替换为一个略小的负值)。
示例:
(number-sequence 4 9)
⇒ (4 5 6 7 8 9)
(number-sequence 9 4 -1)
⇒ (9 8 7 6 5 4)
(number-sequence 9 4 -2)
⇒ (9 7 5)
(number-sequence 8)
⇒ (8)
(number-sequence 8 5)
⇒ nil
(number-sequence 5 8 -1)
⇒ nil
(number-sequence 1.5 6 2)
⇒ (1.5 3.5 5.5)
没有严格等价的方法向列表尾部添加元素。可使用 (append listname (list newelt)),复制 listname 并在尾部添加 newelt,生成全新列表;或使用 (nconc listname (list newelt)),遍历 listname 的所有 CDR,替换末尾的 nil,直接修改 listname。对比之下,用 cons 向列表头部添加元素时,既不复制也不修改原列表。