我来自命令式背景,但这些天我尝试使用 LISP(Common LISP)
我在这里读到了cons
(cons x L):
给定一个 LISP 对象 x 和一个列表 L,计算 (cons x L) 创建一个包含 x 和 L 中的元素的列表。
当我故意不使用列表作为第二个参数时,即当我使用
(cons 'a 'a)
我期待一个错误,但哇!我得到了(A . A)
。
我错过了什么,什么是 (A . A)
?
我来自命令式背景,但这些天我尝试使用 LISP(Common LISP)
我在这里读到了cons
(cons x L):
给定一个 LISP 对象 x 和一个列表 L,计算 (cons x L) 创建一个包含 x 和 L 中的元素的列表。
当我故意不使用列表作为第二个参数时,即当我使用
(cons 'a 'a)
我期待一个错误,但哇!我得到了(A . A)
。
我错过了什么,什么是 (A . A)
?
Cons
构造一个“cons cell”。这最初与列表无关。一个 cons 单元格是一对两个值。一个 cons 单元格以书面形式由“点对”表示,例如(A . B)
,它保存两个值'A
和'B
。
cons 单元格中的两个位置称为“car”和“cdr”。您可以将这样的 cons 单元可视化为一个二等分的块:
car cdr
+-----+-----+
| A | B |
+-----+-----+
在 Lisp 中,一个值也可以是对其他东西的引用,例如,另一个 cons 单元格:
+-----+-----+ +-----+-----+
| A | --------> | B | C |
+-----+-----+ +-----+-----+
这将以“点对”形式表示为(A . (B . C))
。你可以这样继续:
+-----+-----+ +-----+-----+ +-----+-----+
| A | --------> | B | --------> | C | D |
+-----+-----+ +-----+-----+ +-----+-----+
这是(A . (B . (C . D)))
. 如您所见,在这样的结构中,值始终位于car
cons 单元格中,并且cdr
指向结构的其余部分。一个例外是最后一个值,它位于 last 中cdr
。不过,我们不需要这个例外:NIL
在 Lisp 中有一个特殊的值,它表示“无”。通过放入NIL
last cdr
,您有一个方便的哨兵值,并且您的所有值都在car
s 中:
+-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+
| A | --------> | B | --------> | C | --------> | D | NIL |
+-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+
这就是在 Lisp 中构建列表的方式。由于(A . (B . (C . (D . NIL))))
有点笨拙,也可以简单地表示为(A B C D)
。 NIL
也称为空列表()
;这些是同一事物的可交换符号。
现在您可以看到为什么(cons x list)
返回另一个列表。 简单地用inCons
构造另一个 cons 单元格,并在中引用:x
car
list
cdr
+-----+-----+
| X | --------> list
+-----+-----+
如果list
是(A B)
,则结果为:
+-----+-----+ +-----+-----+ +-----+-----+
| X | --------> | A | --------> | B | NIL |
+-----+-----+ +-----+-----+ +-----+-----+
因此,(cons x '(a b))
评估为(x a b)
。
列表只是 cons 单元格的一种非常常见的用法。实际上,您还可以从 cons 单元格、循环列表或任何有向图构造任意树。
'a
是一个 lisp atom 并且(A . A)
是一个退化列表,称为cons 单元或“点对”。由于您没有传递参数列表L
,因此(cons x L)
您返回了一个单元格。
(缺点 x L)
给定 x 和 L,CONS 返回一个新的 cons 单元,其中 x 作为该单元的 CAR,L 作为该单元的 CDR。
列表是 cons 单元的链接链。
CL-USER 141 > (sdraw '(a b c))
[*|*]--->[*|*]--->[*|*]---> NIL
| | |
v v v
A B C
CL-USER 142 > (sdraw (cons 'foo '(a b c)))
[*|*]--->[*|*]--->[*|*]--->[*|*]---> NIL
| | | |
v v v v
FOO A B C
如果 CONS 得到两个符号作为参数,它看起来像这样:
CL-USER 143 > (sdraw (cons 'foo 'bar))
[*|*]---> BAR
|
v
FOO