Cons 构造一个“cons 单元格”。这最初与列表无关。一个 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)))。如您所见,在这样的结构中,值始终位于 cons 单元格的 car 中,而 cdr 指向结构的其余部分。一个例外是最后一个值,它在最后一个cdr 中。不过,我们不需要这个例外:在 Lisp 中有一个特殊的值 NIL,它表示“无”。通过将NIL 放入最后一个cdr,您就有了一个方便的标记值,并且所有您的值都在cars 中:
+-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+
| A | --------> | B | --------> | C | --------> | D | NIL |
+-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+
这就是在 Lisp 中构建列表的方式。由于(A . (B . (C . (D . NIL)))) 有点笨拙,也可以简单地表示为(A B C D)。 NIL也叫空列表();这些是同一事物的可交换符号。
现在您可以看到为什么(cons x list) 返回另一个列表了。 Cons 只是在car 中使用x 和在cdr 中对list 的引用构造另一个cons 单元格:
+-----+-----+
| X | --------> list
+-----+-----+
如果list 是(A B),则结果为:
+-----+-----+ +-----+-----+ +-----+-----+
| X | --------> | A | --------> | B | NIL |
+-----+-----+ +-----+-----+ +-----+-----+
所以,(cons x '(a b)) 的计算结果为 (x a b)。
列表只是 cons 单元格的一种非常常见的用法。实际上,您还可以从 cons 单元格、循环列表或任何有向图构造任意树。