Lisp cons()的意外输出

Lisp cons()的意外输出,lisp,common-lisp,cons,Lisp,Common Lisp,Cons,我来自一个命令式的背景,但这些天我在尝试LISP(Common LISP) 我读到了关于cons (cons x L): 给定一个LISP对象x和一个列表L,求值(cons x L)将创建一个包含x的列表,后跟L中的元素 当我故意不使用列表作为第二个参数时,即当我使用 (cons'a'a)我以为会出错,但哇!我得到了(A.A) 我错过了什么,什么是(A.A)?'A是一个lisp原子,(A.A)是一个称为或“点对”的退化列表。由于您没有在(cons x L)中传递参数L的列表,因此返回了一个单元格

我来自一个命令式的背景,但这些天我在尝试LISP(Common LISP)

我读到了关于cons

(cons x L):

给定一个LISP对象x和一个列表L,求值(cons x L)将创建一个包含x的列表,后跟L中的元素

当我故意不使用列表作为第二个参数时,即当我使用

(cons'a'a)
我以为会出错,但哇!我得到了
(A.A)


我错过了什么,什么是
(A.A)

'A
是一个lisp原子,
(A.A)
是一个称为或“点对”的退化列表。由于您没有在
(cons x L)
中传递参数
L
的列表,因此返回了一个单元格。

(cons 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
+-----+-----+       +-----+-----+       +-----+-----+
|  A  |   --------> |  B  |   --------> |  C  |  D  |
+-----+-----+       +-----+-----+       +-----+-----+

Cons
构造一个“Cons单元”。起初,这与列表无关。cons单元格是由两个值组成的一对。cons单元格以书面形式由“点对”表示,例如
(A.B)
,它包含两个值
'A
'B

cons单元中的两个位置称为“car”和“cdr”。可以将此类cons单元可视化为对分块:

  car   cdr
+-----+-----+
|  A  |  B  |
+-----+-----+
在Lisp中,值也可以是对其他内容的引用,例如,另一个cons单元格:

+-----+-----+       +-----+-----+
|  A  |   --------> |  B  |  C  |
+-----+-----+       +-----+-----+
这将以“点对”形式表示为
(A.(B.C))
。您可以这样继续:

CL-USER 143 > (sdraw (cons 'foo 'bar))

[*|*]---> BAR
  |
  v
 FOO
+-----+-----+       +-----+-----+       +-----+-----+
|  A  |   --------> |  B  |   --------> |  C  |  D  |
+-----+-----+       +-----+-----+       +-----+-----+
这是
(A.(B.(C.D))
。如您所见,在这种结构中,值始终位于cons单元格的
car
中,
cdr
指向结构的其余部分。例外情况是最后一个值,它位于最后一个
cdr
中。不过,我们不需要这个异常:在Lisp中有一个特殊的值
NIL
,表示“nothing”。通过将
NIL
放入最后一个
cdr
,您就有了一个方便的哨兵值,并且您的所有值都在
车中
s:

+-----+-----+       +-----+-----+       +-----+-----+       +-----+-----+
|  A  |   --------> |  B  |   --------> |  C  |   --------> |  D  | NIL |
+-----+-----+       +-----+-----+       +-----+-----+       +-----+-----+
这就是Lisp中列表的构造方式。由于
(A.(B.(C.(D.NIL))
有点笨拙,它也可以简单地表示为
(A.B.C.D)
NIL
也称为空列表
()
;这些是相同事物的可交换符号

现在您可以看到为什么
(cons x list)
返回另一个列表
Cons
只需在
car
中用
x
cdr
中对
列表的引用构造另一个Cons单元格:

+-----+-----+
|  X  |   --------> list
+-----+-----+
如果
列表
(A B)
,则其计算结果如下:

+-----+-----+       +-----+-----+       +-----+-----+
|  X  |   --------> |  A  |   --------> |  B  | NIL |
+-----+-----+       +-----+-----+       +-----+-----+
因此,
(cons x'(ab))
的计算结果为
(xab)

列表只是cons单元格的一个非常常见的用途。实际上,您还可以从cons单元格、循环列表或任何有向图构造任意树