7.1 carcdr

列表的 CAR(首元素)简单地就是列表中的第一个项。因此,列表 (rose violet daisy buttercup)CAR 就是 rose

如果你在 GNU Emacs 的 Info 中阅读此文档,你可以通过执行以下命令来验证:

(car '(rose violet daisy buttercup))

在执行此表达式之后,rose 将会出现在回显区域。

car 不会从列表中删除第一个项;它只是报告这个项是什么。在术语中,car 是“非破坏性”的。这一特性事实上变得很重要。

列表的 CDR(剩余元素)是列表中剩下的部分,即 cdr 函数返回紧随第一个项之后的列表部分。因此,虽然列表 '(rose violet daisy buttercup)CARrose,但列表的其余部分,即 cdr 函数返回的值,是 (violet daisy buttercup)

你可以通过以通常的方式执行以下命令来验证这一点:

(cdr '(rose violet daisy buttercup))

当你执行此命令时,(violet daisy buttercup) 将会出现在回显区域。

car 一样,cdr 也不会从列表中移除任何元素,它只是返回第二个及后续元素的报告。

顺便说一下,在例子中,花卉列表是被引用的。如果没有引用,Lisp 解释器会尝试通过调用 rose 作为函数来评估列表。在这个例子中,我们不想这样做。

在处理列表时,使用 firstrest 这样的名称可能比 carcdr 更有意义。事实上,一些程序员会将 firstrest 定义为 carcdr 的别名,然后在代码中使用 firstrest

然而,在Lisp中,列表是使用称为“cons单元”(see 如何实现列表)的较低级结构构建的,在其中“第一个”或“剩余”这样的概念是不存在的,而 CARCDR 是对称的。Lisp 不会隐藏 cons 单元的存在,程序也会将它们用于除列表之外的其他事物。因此,这些名称有助于提醒程序员,尽管在列表中它们是不对称使用的,但 carcdr 实际上是对称的。

carcdr 应用于由符号组成的列表时(例如,列表 (pine fir oak maple)),函数 car 返回的列表元素是符号 pine 而没有括号。pine 是列表中的第一个元素。然而,列表的 CDR 本身是一个列表,即 (fir oak maple),你可以通过以通常的方式执行以下表达式来验证:

(car '(pine fir oak maple))

(cdr '(pine fir oak maple))

另一方面,在列表的列表中,第一个元素本身是一个列表。例如,以下列表包含三个子列表,一个肉食动物列表,一个食草动物列表和一个海洋哺乳动物列表:

(car '((lion tiger cheetah)
       (gazelle antelope zebra)
       (whale dolphin seal)))

在这个例子中,列表的第一个元素或 CAR 是肉食动物列表 (lion tiger cheetah),而列表的其余部分是 ((gazelle antelope zebra) (whale dolphin seal))

(cdr '((lion tiger cheetah)
       (gazelle antelope zebra)
       (whale dolphin seal)))

值得再次强调的是,carcdr 是非破坏性的——也就是说,它们不会修改或改变应用到它们的列表。这对它们的使用非常重要。

另外,在第一章中,关于原子的讨论中,我提到在Lisp中,某些类型的原子,例如数组,可以被分解成部分;但这个机制与拆分列表的机制不同。就Lisp而言,列表的原子是不可分割的。(See Lisp原子.) carcdr 函数用于拆分列表,被认为是Lisp的基本功能。由于它们不能拆分或访问数组的部分,数组被认为是原子。相反,另一个基本函数 cons 可以组合或构造列表,但不能构造数组。 (数组由数组特定的函数处理。See Arrays in The GNU Emacs Lisp Reference Manual.)