就它们的作用而言,car和cdr相当于first和rest。这在文档中非常清楚。HyperSpec 在first、second和 &c 的条目上说:
第一个、第二个、第三个、第四个、第五个、第六个、第七个、第八个、第九个和第十个函数分别访问列表的第一个、第二个、第三个、第四个、第五个、第六个、第七个、第八个、第九个和第十个元素。具体来说,
(first list) == (car list)
(second list) == (car (cdr list))
(third list) == (car (cddr list))
…
笔记:
第一个在功能上等同于汽车,第二个在功能上等同于 cadr,第三个在功能上等同于 caddr,第四个在功能上等同于 cadddr。
现在,当您使用这些功能时,存在差异,不是在功能上,而是在风格上。这实际上也在 HyperSpec 中被调用,例如,在rest条目中:
笔记:
当论点被主观地视为一个列表而不是一个缺点时,rest 在风格上通常比 cdr 更受欢迎。
例如,考虑两种映射从 cons 单元构建的结构的方法。首先,我们在一个 cons 单元树上进行映射,对树的每个叶子(即非 cons)调用一些函数。我们用consp检查某个东西是否是 cons ,如果是,我们递归到它的car和cdr上。我们通过调用cons将结果合并到一个新的 cons 单元格中。
(defun map-over-cons (function tree)
(if (not (consp tree))
(funcall function tree)
(cons (map-over-cons function (car tree))
(map-over-cons function (cdr tree)))))
或者,当我们映射一个列表时,我们通常使用endp(或null,但endp强调我们正在寻找列表的结尾,而不仅仅是寻找nil)检查终止条件,我们调用该函数列表的第一个并递归到列表的其余部分。虽然看到使用cons构造的结果很常见,但实际上list*会在使用两个参数调用时执行相同的任务(通常,它可以做更多),强调正在构造一个列表:
(defun map-over-list (function list)
(if (endp list)
'()
(list* (funcall function (first list))
(map-over-list function (rest list)))))
这些函数中的任何一个都可以使用car、cdr和cons编写,或者使用first、rest和list*或它们的任意组合编写,但是坚持使用其中一个可以帮助以后可能阅读代码的人(包括原始代码)作者),并表明作者的意图。
以及如何最终使用 CDR、CAR 实现 FIRST/REST?
怎么样:
(defun first (x) (car x))
(defun rest (x) (cdr x))
或者甚至更好,如果你有符号功能:
(setf (symbol-function 'first) (symbol-function 'car))
(setf (symbol-function 'rest) (symbol-function 'cdr))