以下是一些通过修改组成列表的 cons 单元的 CDR,以破坏性方式重排列表的函数。这些函数之所以具有破坏性,是因为它们会 “拆解” 传入的原始列表参数,重新链接其 cons 单元,形成一个新列表并将其作为返回值。
另一个修改 cons 单元的函数可参见《将列表用作集合》章节中的 delq。
该函数返回一个包含所有 lists 中元素的列表。与 append(see 构建 cons 单元与列表)不同,nconc 不会复制这些列表;相反,它会修改每个列表的最后一个 CDR,使其指向后一个列表。lists 的最后一个不会被修改。示例如下:
(setq x (list 1 2 3))
⇒ (1 2 3)
(nconc x '(4 5))
⇒ (1 2 3 4 5)
x
⇒ (1 2 3 4 5)
由于 nconc 的最后一个参数本身不会被修改,因此像上面的示例一样使用 constant list常量列表 (如 '(4 5))是合理的。同理,最后一个参数也不必是列表:
(setq x (list 1 2 3))
⇒ (1 2 3)
(nconc x 'z)
⇒ (1 2 3 . z)
x
⇒ (1 2 3 . z)
但其他参数(除最后一个外)应当是可变列表。它们也可以是点列表(dotted list),此时其最后一个 CDR 会被替换为下一个参数:
(nconc (cons 1 2) (cons 3 (cons 4 5)) 'z)
⇒ (1 3 4 . z)
一个常见的陷阱是将常量列表用作 nconc 的非最后一个参数。如果这样做,程序的行为将是未定义的(see 自求值形式)—— 甚至可能每次运行程序时得到的结果都不同!以下是可能出现的情况(但不保证一定会发生):
(defun add-foo (x) ; 期望该函数在参数开头添加 foo
(nconc '(foo) x))
(symbol-function 'add-foo) ; 访问符号的函数单元。即获取符号绑定的函数
⇒ #f(lambda (x) [t] (nconc '(foo) x))
(setq xx (add-foo '(1 2))) ; 看起来运行正常
⇒ (foo 1 2)
(setq xy (add-foo '(3 4))) ; 出了什么问题?
⇒ (foo 1 2 3 4)
(eq xx xy)
⇒ t
(symbol-function 'add-foo)
⇒ #f(lambda (x) [t] (nconc '(foo 1 2 3 4) x))