CDR、CAR 和 REST、FIRST 和可能的实现之间的区别?

Difference between CDR, CAR and REST, FIRST and possible implementation?

我正在学习一些关于 LISP 函数式编程的知识,以下是我遇到的内容:LISP 使用 CAR、CDR 函数以及 FIRST 和 REST 函数。两者都与列表有关。

据我目前所了解的,这两者之间是有区别的,但我不太明白有什么区别。

谁能帮我总结一下?我如何最终使用 CDR、CAR 实施 FIRST/REST?


编辑:由于接受的答案提到了文档,但没有 link,这里是 link CAR/CDR, here then for FIRST/REST 的文档。

此外 - 重要提示 - linked 文档是 "just implementation notes" CLISP,这是一个常用的环境。一般来说,几乎不可能为这种语言找到 "official documentations"。

传统上,car 和 cdr 更面向机器,而 first 和 rest 则是更抽象的函数。实际上,它们之间没有区别。大家都坚持car和cdr,所以car和cdr占了上风。

如果您很难找到 car 和 first 之间的任何差异,那是因为存在 none。先看成car的别名。

定义?

(defun first (x) (car x))
(defun rest (x) (cdr x))

首先,其中 none 是谓词(或者至少,它们不是 Lisp 程序员所说的 "predicates";在这种情况下,"predicate" 表示 "a function that returns a boolean value").

关于这个问题,让我们跳进 REPL 一分钟。

; SLIME 2014-12-23
CL-USER> (describe #'car)
#<FUNCTION CAR>
  [compiled function]

Lambda-list: (LIST)
Declared type: (FUNCTION (LIST) (VALUES T &OPTIONAL))
Documentation:
  Return the 1st object in a list.
Known attributes: foldable, flushable, unsafely-flushable
Source file: SYS:SRC;CODE;LIST.LISP
; No value
CL-USER> (describe #'first)
#<FUNCTION FIRST>
  [compiled function]

Lambda-list: (LIST)
Declared type: (FUNCTION (LIST) (VALUES T &OPTIONAL))
Documentation:
  Return the 1st object in a list or NIL if the list is empty.
Known attributes: foldable, flushable, unsafely-flushable
Source file: SYS:SRC;CODE;LIST.LISP
; No value
CL-USER> (describe #'cdr)
#<FUNCTION CDR>
  [compiled function]

Lambda-list: (LIST)
Declared type: (FUNCTION (LIST) (VALUES T &OPTIONAL))
Documentation:
  Return all but the first object in a list.
Known attributes: foldable, flushable, unsafely-flushable
Source file: SYS:SRC;CODE;LIST.LISP
; No value
CL-USER> (describe #'rest)
#<FUNCTION REST>
  [compiled function]

Lambda-list: (LIST)
Declared type: (FUNCTION (LIST) (VALUES T &OPTIONAL))
Documentation:
  Means the same as the cdr of a list.
Known attributes: foldable, flushable, unsafely-flushable
Source file: SYS:SRC;CODE;LIST.LISP
; No value

因此,根据文档及其签名,car 等同于 firstcdr 等同于 rest。让我们测试一下。

CL-USER> (cons 1 2)
(1 . 2)
CL-USER> (car (cons 1 2))
1
CL-USER> (first (cons 1 2))
1
CL-USER> (cdr (cons 1 2))
2
CL-USER> (rest (cons 1 2))
2
CL-USER> (cons 1 nil)
(1)
CL-USER> (car (cons 1 nil))
1
CL-USER> (first (cons 1 nil))
1
CL-USER> (cdr (cons 1 nil))
NIL
CL-USER> (rest (cons 1 nil))
NIL
CL-USER> nil
NIL
CL-USER> (car nil)
NIL
CL-USER> (first nil)
NIL
CL-USER> (cdr nil)
NIL
CL-USER> (rest nil)
NIL

所以,他们似乎是一样的。

操作 firstrest 表明您正在使用列表:一系列以空列表结尾的对,即它的形式为 (list x1 ... xn)

操作 carcdr 表示您正在使用对构建数据结构,这可能不是列表。

即在使用列表时选择 firstrest,以使代码更易于其他人阅读。

就他们所做的而言,carcdr 等同于 first休息。这在文档中非常清楚。 HyperSpec 在 firstsecond、&c:

的条目上说

The functions first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, and tenth access the first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, and tenth elements of list, respectively. Specifically,

(first list)    ==   (car list)
(second list)   ==   (car (cdr list))
(third list)    ==   (car (cddr list))

Notes:

first is functionally equivalent to car, second is functionally equivalent to cadr, third is functionally equivalent to caddr, and fourth is functionally equivalent to cadddr.

现在,当您使用这些功能时, 的区别不在于功能,而在于风格。这实际上也在 HyperSpec 中被调用,例如,在 rest:

的条目中

Notes:

rest is often preferred stylistically over cdr when the argument is to being subjectively viewed as a list rather than as a cons.

例如,考虑两种映射从 cons 单元构建的结构的方法。首先,我们映射到一个 cons 单元格,用树的每个叶子(即非 cons)调用一些函数。我们用 consp 检查某物是否有缺点,如果是,我们递归到它的 carcdr.我们通过调用 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 强调我们正在寻找 listend,而不仅仅是寻找 nil),我们在列表的 first 调用函数并递归到列表的 rest。虽然看到使用 cons 构造的结果很常见,但实际上 list* 在使用两个参数调用时将执行相同的任务(通常,它可以做更多) 强调正在构建 list:

(defun map-over-list (function list)
  (if (endp list)
      '()
      (list* (funcall function (first list))
             (map-over-list function (rest list)))))

这些函数中的任何一个都可以使用 carcdrcons 编写,或使用 firstrestlist*,或它们的任意组合,但坚持使用一个或另一个帮助以后可能阅读代码的人(包括原作者),并表明作者的意图

And how do I eventually implement FIRST/REST using CDR, CAR?

怎么样:

(defun first (x) (car x))
(defun rest (x) (cdr x))

或者可能更好,如果你有 symbol-function:

(setf (symbol-function 'first) (symbol-function 'car))
(setf (symbol-function 'rest) (symbol-function 'cdr))