我正在学习 Lisp,但我在 Lisp 编程方面没有经验。在我的部分学习中,我遇到了以下示例:
> (cons ‘a ‘(a b)) ----> (A A B)
> (cons ‘(a b) ‘a) ----> ((A B).A)
我想知道为什么当我们有(cons 'a '(ab))时响应是(AAB)以及为什么当我们稍微改变它并将'a放在(ab)之后时,响应是一个点列表,例如((AB ).A) ? 第一个代码行和第二个代码行有什么区别?这些代码背后发生了什么?
我正在学习 Lisp,但我在 Lisp 编程方面没有经验。在我的部分学习中,我遇到了以下示例:
> (cons ‘a ‘(a b)) ----> (A A B)
> (cons ‘(a b) ‘a) ----> ((A B).A)
我想知道为什么当我们有(cons 'a '(ab))时响应是(AAB)以及为什么当我们稍微改变它并将'a放在(ab)之后时,响应是一个点列表,例如((AB ).A) ? 第一个代码行和第二个代码行有什么区别?这些代码背后发生了什么?
如果您将它们视为cons-cells,则很容易理解。
简而言之,一个 cons 单元格正好包含两个值。对此的正常表示法是使用点,例如:
(cons 'a 'b) ==> (A . B)
但由于列表在 LISP 中经常使用,更好的表示法是去掉点。列表是通过让第二个元素成为一个新的 cons 单元格而创建的,最后一个结尾是一个终止符(通常是 nil,或者'()
在 Common Lisp 中)。所以这两个是相等的:
(cons 'a (cons 'b '())) ==> (A B)
(list 'a 'b) ==> (A B)
因此(cons 'a 'b)
创建了一个单元格[a,b]
,(list 'a 'b)
并将创建[a, [b, nil]]
. 请注意在 cons 单元格中编码列表的约定:它们以内部nil
.
现在,如果你 cons'a
到最后一个列表,你将创建一个新的 cons 单元格,其中包含[[a, [b, nil]], a]
. 由于这不是一个“正确的”列表,即它不是以 a 结尾的nil
,因此写出它的方法是使用点:(cons '(a b) 'a) ==> ((a b) . a)
。
如果没有打印点,它必须是一个具有结构的列表[[a, [b, nil]], [a, nil]]
。
你的例子
当你这样做(cons 'a '(a b))
时,它将获取符号'a
和列表'(a b)
并将它们放入一个新的 cons 单元格中。所以这将包括[a, [a, [b, nil]]]
. 由于这自然以 inner 结尾nil
,因此它是不带点的。
至于(cons '(a b) 'a)
,现在你会得到[[a, [b, nil]], a]
的。这不会以内部结尾nil
,因此将使用点符号。
我们可以使用 cons 使最后一个示例以内部 nil 结尾吗?是的,如果我们这样做
(cons '(a b) (cons 'a '())) ==> ((A B) A)
最后,
(list '(a b) 'a))
相当于
(cons (cons (cons 'a (cons 'b '())) (cons 'a '())))
看到这个可视化:
CL-USER 7 > (sdraw:sdraw '(A A B))
[*|*]--->[*|*]--->[*|*]--->NIL
| | |
v v v
A A B
CL-USER 8 > (sdraw:sdraw '((A B) . A))
[*|*]--->A
|
v
[*|*]--->[*|*]--->NIL
| |
v v
A B
还:
CL-USER 9 > (sdraw:sdraw '(A B))
[*|*]--->[*|*]--->NIL
| |
v v
A B
CL-USER 10 > (sdraw:sdraw (cons 'A '(A B)))
[*|*]--->[*|*]--->[*|*]--->NIL
| | |
v v v
A A B
CL-USER 11 > (sdraw:sdraw (cons '(A B) 'A))
[*|*]--->A
|
v
[*|*]--->[*|*]--->NIL
| |
v v
A B
Acons
是一个可以包含两个值的数据结构。例如(cons 1 2) ; ==> (1 . 2)
。第一部分是car
,第二部分是cdr
。Acons
是 alist
如果它cdr
是nil
or 或 a list
。因此
(1 . (2 . (3 . ())))
是一个列表。
打印时,如果是 a或cons
,则省略点。的外括号也被省略。因而被打印和被打印。它是相同的结构,但具有不同的可视化。A中没有这个规则。cdr
cons
nil
cdr
(3 . ())
(3)
(1 . (2 . (3 . ())))
(1 2 3)
cons
car
当是一个列表时,读取函数cons
使用点和奇怪的异常打印格式读取。cdr
它将在读取时表现得好像它是cons
.
两者都有一个特殊规则,read
即使print
它是cons
.
(cons ‘a ‘(a b)) ----> (A . (A B))
(cons ‘(a b) ‘a) ----> ((A B) . A)
打印时,第一个是一个包含 3 个元素的列表,因为它cdr
是一个列表。
一个列表 (abc) 被表示(在内部存储)为三个 cons-cells (cons 'a (cons 'b (cons 'c '())
:. 请注意,最后一对在其 cdr 中有 '()。
最后一个 cdr 为 '() 的一系列 cons-cell 被打印机打印为列表。该示例因此打印为 (abc)。
我们来看看: (cons 'a '(a b))
。
列表 '(ab) 表示为 (cons 'a (cons 'b '())。这意味着
(cons 'a '(a b))
产生:(cons 'a (cons 'a (cons 'b '()))
。
我们来看看:(cons '(a b) 'a)
。
列表 '(ab) 表示为 (cons 'a (cons 'b '())。这意味着
(cons (cons '(a b) 'a))
产生(cons (cons 'a (cons 'b '()) 'a)
.
请注意,本系列不以'()
. 显示打印机使用点表示法。( ... . 'a)
表示一个值由一系列 cons-cells 组成,并且最后一个 cdr 包含'a
. 该值(cons (cons 'a (cons 'b '()) 'a)
因此打印为'((a b) . a)
。
cons 只是一个 pair 数据类型。例如,(cons 1 2)
是一对1
and 2
,它将打印两个元素,由一个点分隔,如(1 . 2)
。
列表在内部表示为嵌套的 conses,例如列表(1 2 3)
是(cons 1 (cons 2 (cons 3 '()))
.