car
和 cdr
列表的 CAR(首元素)简单地就是列表中的第一个项。因此,列表 (rose violet daisy buttercup)
的 CAR 就是 rose
。
如果你在 GNU Emacs 的 Info 中阅读此文档,你可以通过执行以下命令来验证:
(car '(rose violet daisy buttercup))
在执行此表达式之后,rose
将会出现在回显区域。
car
不会从列表中删除第一个项;它只是报告这个项是什么。在术语中,car
是“非破坏性”的。这一特性事实上变得很重要。
列表的 CDR(剩余元素)是列表中剩下的部分,即 cdr
函数返回紧随第一个项之后的列表部分。因此,虽然列表 '(rose violet daisy buttercup)
的 CAR 是 rose
,但列表的其余部分,即 cdr
函数返回的值,是 (violet daisy buttercup)
。
你可以通过以通常的方式执行以下命令来验证这一点:
(cdr '(rose violet daisy buttercup))
当你执行此命令时,(violet daisy buttercup)
将会出现在回显区域。
与 car
一样,cdr
也不会从列表中移除任何元素,它只是返回第二个及后续元素的报告。
顺便说一下,在例子中,花卉列表是被引用的。如果没有引用,Lisp 解释器会尝试通过调用 rose
作为函数来评估列表。在这个例子中,我们不想这样做。
在处理列表时,使用 first
和 rest
这样的名称可能比 car
和 cdr
更有意义。事实上,一些程序员会将 first
和 rest
定义为 car
和 cdr
的别名,然后在代码中使用 first
和 rest
。
然而,在Lisp中,列表是使用称为“cons单元”(see 如何实现列表)的较低级结构构建的,在其中“第一个”或“剩余”这样的概念是不存在的,而 CAR 和 CDR 是对称的。Lisp 不会隐藏 cons 单元的存在,程序也会将它们用于除列表之外的其他事物。因此,这些名称有助于提醒程序员,尽管在列表中它们是不对称使用的,但 car
和 cdr
实际上是对称的。
当 car
和 cdr
应用于由符号组成的列表时(例如,列表 (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)))
值得再次强调的是,car
和 cdr
是非破坏性的——也就是说,它们不会修改或改变应用到它们的列表。这对它们的使用非常重要。
另外,在第一章中,关于原子的讨论中,我提到在Lisp中,某些类型的原子,例如数组,可以被分解成部分;但这个机制与拆分列表的机制不同。就Lisp而言,列表的原子是不可分割的。(See Lisp原子.) car
和 cdr
函数用于拆分列表,被认为是Lisp的基本功能。由于它们不能拆分或访问数组的部分,数组被认为是原子。相反,另一个基本函数 cons
可以组合或构造列表,但不能构造数组。 (数组由数组特定的函数处理。See Arrays in The GNU Emacs Lisp Reference Manual.)