"Cons" 在 Lisp 中如何工作?
How does "Cons" work in Lisp?
我正在学习 Lisp,但我没有 Lisp 编程经验。在我的部分学习中,我遇到了以下示例:
> (cons ‘a ‘(a b)) ----> (A A B)
> (cons ‘(a b) ‘a) ----> ((A B).A)
我想知道为什么当我们有 (cons 'a '(a b)) 时响应是 (A A B) 以及为什么什么时候我们稍微改变一下,将 'a 放在 (a b) 之后,响应是一个虚线列表,如 ((A B ).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
.
终止
现在,如果您将 'a
添加到最后一个列表中,您将创建一个包含 [[a, [b, nil]], a]
的新 cons 单元格。因为这不是 "proper" 列表,即它不是以 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]]]
。由于这自然以内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 '())))
列表 (a b c) 表示(内部存储)为三个cons-cells:(cons 'a (cons 'b (cons 'c '())
。请注意,最后一对的 cdr.
中有 '()
打印机将最后一个 cdr 为 '() 的一系列 cons-cells 打印为列表。因此,该示例打印为 (a b c).
让我们看看:(cons 'a '(a b))
。
列表'(a b)表示为(cons 'a (cons 'b '())。这意味着
(cons 'a '(a b))
产生:(cons 'a (cons 'a (cons 'b '()))
.
我们来看看:(cons '(a b) 'a)
.
列表'(a b)表示为(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)
.
查看此可视化:
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
一个cons
是一个可以包含两个值的数据结构。例如(cons 1 2) ; ==> (1 . 2)
。第一部分是 car
,第二部分是 cdr
。如果 cdr
是 nil
或 list
,则 cons
是 list
。因此
(1 . (2 . (3 . ())))
是一个列表。
当打印 cons
时,当 cdr
是 cons
或 nil
时省略点。 cdr
的外括号也被省略。因此 (3 . ())
打印为 (3)
,(1 . (2 . (3 . ())))
打印为 (1 2 3)
。它是相同的结构,但具有不同的可视化效果。 car
中的一个cons
没有这个规则。
当 cdr
是一个列表时,读取函数用点和奇怪的异常打印格式读取 cons
。它在读取时的行为就像 cons
.
对于 read
和 print
都有一个特殊的规则,即使它是 cons
的链,列表的错觉也是完整的。
(cons ‘a ‘(a b)) ----> (A . (A B))
(cons ‘(a b) ‘a) ----> ((A B) . A)
打印时,第一个是一个包含 3 个元素的列表,因为 cdr
是一个列表。
cons 只是一对数据类型。例如,(cons 1 2)
是一对 1
和 2
,打印时两个元素之间用点分隔,如 (1 . 2)
.
列表在内部表示为嵌套的conses,例如列表 (1 2 3)
是 (cons 1 (cons 2 (cons 3 '()))
。
我正在学习 Lisp,但我没有 Lisp 编程经验。在我的部分学习中,我遇到了以下示例:
> (cons ‘a ‘(a b)) ----> (A A B)
> (cons ‘(a b) ‘a) ----> ((A B).A)
我想知道为什么当我们有 (cons 'a '(a b)) 时响应是 (A A B) 以及为什么什么时候我们稍微改变一下,将 'a 放在 (a b) 之后,响应是一个虚线列表,如 ((A B ).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
.
现在,如果您将 'a
添加到最后一个列表中,您将创建一个包含 [[a, [b, nil]], a]
的新 cons 单元格。因为这不是 "proper" 列表,即它不是以 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]]]
。由于这自然以内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 '())))
列表 (a b c) 表示(内部存储)为三个cons-cells:(cons 'a (cons 'b (cons 'c '())
。请注意,最后一对的 cdr.
打印机将最后一个 cdr 为 '() 的一系列 cons-cells 打印为列表。因此,该示例打印为 (a b c).
让我们看看:(cons 'a '(a b))
。
列表'(a b)表示为(cons 'a (cons 'b '())。这意味着
(cons 'a '(a b))
产生:(cons 'a (cons 'a (cons 'b '()))
.
我们来看看:(cons '(a b) 'a)
.
列表'(a b)表示为(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)
.
查看此可视化:
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
一个cons
是一个可以包含两个值的数据结构。例如(cons 1 2) ; ==> (1 . 2)
。第一部分是 car
,第二部分是 cdr
。如果 cdr
是 nil
或 list
,则 cons
是 list
。因此
(1 . (2 . (3 . ())))
是一个列表。
当打印 cons
时,当 cdr
是 cons
或 nil
时省略点。 cdr
的外括号也被省略。因此 (3 . ())
打印为 (3)
,(1 . (2 . (3 . ())))
打印为 (1 2 3)
。它是相同的结构,但具有不同的可视化效果。 car
中的一个cons
没有这个规则。
当 cdr
是一个列表时,读取函数用点和奇怪的异常打印格式读取 cons
。它在读取时的行为就像 cons
.
对于 read
和 print
都有一个特殊的规则,即使它是 cons
的链,列表的错觉也是完整的。
(cons ‘a ‘(a b)) ----> (A . (A B))
(cons ‘(a b) ‘a) ----> ((A B) . A)
打印时,第一个是一个包含 3 个元素的列表,因为 cdr
是一个列表。
cons 只是一对数据类型。例如,(cons 1 2)
是一对 1
和 2
,打印时两个元素之间用点分隔,如 (1 . 2)
.
列表在内部表示为嵌套的conses,例如列表 (1 2 3)
是 (cons 1 (cons 2 (cons 3 '()))
。