我意识到这是一个完全 n00b 的问题,但我很好奇,我想我可能会在这里得到比其他任何地方更好的解释。这是一个列表(我正在使用 Dr. Scheme)
> (list 1 2 3)
(1 2 3)
我认为这只是糖:
> (cons 1 (cons 2 (cons 3 null)))
(1 2 3)
另一方面,这会做其他事情:
> (cons 1 (cons 2 3))
(1 2 . 3)
我的问题是,为什么会有不同?在列表末尾要求 null 有什么意义?
列表的定义是递归的。
1. The null list (empty list) is a list
2. A list is made up of an item cons a list
所以这些是列表:
1. null => () --read as empty list
2. cons 3 null => (3)
3. cons2 (cons 3 null) => (2, 3)
您给出的最后一个示例 cons 2 3 不符合此列表定义,因此它不是列表。那就是 cons 接受一个项目和一个列表。3 不是列表。
cons
将一个新元素添加到列表的开头,所以当你写的时候你在做什么:
(cons 1 (cons 2 (cons 3 null)))
递归地将项目添加到一个不断增长的列表中,从 开始null
,它被定义为空列表 ()
。当您打电话时,(cons 2 3)
您不是从空列表开始,因此不要通过在其开头附加 2 来构造列表。
Lisp,包括 Scheme,是动态类型的,“lisp 方式”是在单个数据结构上拥有许多功能,而不是针对不同任务使用不同的数据结构。
所以问题是“在列表末尾要求 null 有什么意义?” 不太适合问。
该cons
函数不需要您提供cons
对象或nil
作为其第二个参数。如果第二个参数不是cons
对象或nil
,那么您会得到一对而不是列表,并且运行时不会使用列表表示法而是使用点来打印它。
所以如果你想构造一个形状像列表的东西,那么给cons
一个列表作为它的第二个参数。如果你想构造别的东西,那就给cons
别的东西作为它的第二个参数。
如果您想要一个其中恰好有两个值的数据结构,则对很有用。使用一对,你不需要在最后的 nil 来标记它的长度,所以它更有效一些。对列表是键值映射的简单实现;common lisp 具有支持此类属性列表的功能,作为其标准库的一部分。
所以真正的问题是“为什么你可以用相同的函数构造对和列表cons
?”,而答案是“当你只需要一个数据结构时,为什么还要有两个数据结构?”
cons 语句用于分配 car 为 obj1 且 cdr 为 obj2 的对
(cons obj1 obj2)
因此,有必要以 null 结束 cons 语句,以便我们知道我们是列表的末尾。
> (cons 1 (cons 2 3))
(1 2 . 3)
在该示例中,cdr 将是一对 <2,3>,其中 2 是汽车,3 是 cdr。不一样:
(list 1 2 3)