Lisp cons细胞和2-载体有什么区别?

Lisp cons细胞和2-载体有什么区别?,lisp,common-lisp,Lisp,Common Lisp,在有向量的Lisp中,为什么还需要cons单元格?据我所知,cons单元是: 正好有两个元素的结构 命令 访问权限是O1 不过,所有这些也适用于2向量。那么有什么区别呢?cons细胞仅仅是Lisps有载体之前的遗迹吗?或者还有其他我不知道的区别吗?一方面,这是一个实现细节:给定向量,可以使用长度为2的向量实现cons单元格和链表 另一方面,这是一个相当重要的细节:ANSI Common Lisp标准规定类型vector和cons是不相交的,因此,实际上,您不能使用这个技巧来实现ANSI CL。我

在有向量的Lisp中,为什么还需要cons单元格?据我所知,cons单元是:

正好有两个元素的结构 命令 访问权限是O1
不过,所有这些也适用于2向量。那么有什么区别呢?cons细胞仅仅是Lisps有载体之前的遗迹吗?或者还有其他我不知道的区别吗?

一方面,这是一个实现细节:给定向量,可以使用长度为2的向量实现cons单元格和链表


另一方面,这是一个相当重要的细节:ANSI Common Lisp标准规定类型vector和cons是不相交的,因此,实际上,您不能使用这个技巧来实现ANSI CL。

我认为它们是不同的数据结构,例如java有vector和list类。一种适合于随机访问,而列表更适合于顺序访问。所以在任何语言中,向量和列表都可以共存

对于使用您的方法实现Lisp,我认为是可行的,这取决于您的实现细节,但对于ANSI Common Lisp,有一个约定,因为没有列表数据类型:

CL-USER> (type-of (list 1 2 3))
CONS
这是一个缺点,在常见的lisp hypersec中,约定中也提到了类似的内容:

名单。 1.一种conse链,其中每个cons的car是列表中的一个元素,每个cons的cdr是列表中的下一个链接 链或终止原子。另请参见适当列表、虚线列表或 循环清单。 2.null和cons并集的类型

因此,如果使用向量而不是cons创建Lisp,它将不是ANSI CL

因此,您可以创建考虑事物的列表,nil是一个列表,您可以通过考虑创建不同类型的列表:

通常,您会创建一个适当的列表:

(list 1 2 3) = (cons 1 (cons 2 (cons 3 nil)))) = '(1 2 3)
当列表不以nil结尾时,它是一个虚线列表,循环列表有一个对自身的引用

例如,如果我们创建一个字符串common lisp,将其实现为一个简单数组,这对于随机访问比列表更快

CL-USER> (type-of "this is a string")
(SIMPLE-ARRAY CHARACTER (16))
lisp之地关于common lisp的一本好书将cons定义为构建common lisp和处理列表的粘合剂,因此,如果您用其他类似的东西替换cons,您将构建类似于common lisp的东西

最后一个常见lisp序列类型的树,您可以找到完整的

cons细胞仅仅是Lisps有载体之前的遗迹吗

没错。cons、car和cdr是第一个lisp中唯一的复合数据结构的构造函数和访问器。为了区分它和原子类型的符号,一个原子表示符号T,表示符号cons为false。这扩展到其他类型,包括称为数组的向量,见第35页。CommonLisp是基于Lisp1.5的商业Lisp的组合。如果这两种类型都是从一开始就制作的,那么它们可能会有所不同

如果你要做一个普通的Lisp实现,你不需要有两种不同的方法来实现它们,只要你的实现符合规范。如果我没记错的话,我想racket实际上是用vector和vector来实现struct的?对于确实表示对象的向量,重载为f。在CL中,您可以以相同的方式实现defstruct,并实现cons struct以及它需要与hyperspec兼容的函数。在您最喜欢的实现中创建cons时,您可能正在使用向量,甚至不知道它


从历史上看,您仍然使用旧函数,因此John McCarthy代码在第一个lisp出现58年后仍然有效。不需要这样做,但在一种具有现代语言所具有的功能的语言中留下一点遗产并没有坏处。

在典型的通用Lisp实现中,cons单元格将表示为两个机器字,一个用于汽车指针,一个用于cdr指针;事实上,它是一个cons单元格,它被编码在为引用它而构造的指针中。然而,数组是更复杂的对象,除非您有一个专用的T类型的仅两个元素的向量,否则最终将得到一个数组头,其中包含类型信息和大小,此外还需要存储元素所需的存储空间,这些元素可能很难压缩到少于四个机器字


因此,虽然使用两个元素向量/数组作为cons单元格是完全可能的,但使用专用类型会有一定的效率,因为cons单元格和列表在现有的Lisp代码中使用非常频繁。

如果使用两个元素向量,则会将它们的大小和类型存储在列表的每个节点中。 这是荒谬的浪费

您可以通过引入一种特殊的2元素向量类型来避免这种浪费,该类型的元素可以是任何东西


或者换句话说:通过重新引入cons单元。

尽管conse在物理上类似于任何其他两个元素 聚合结构,它们不仅仅是2向量的过时形式

首先,将Lisp中的所有类型划分为cons和atom。只有conse是cons类型;其他一切都是一个原子。向量就是原子

cones构成嵌套列表的表示基础,当然,嵌套列表用于编写代码。它们有一个特殊的打印符号,这样cons 1 cons 2 nil生成的对象可以方便地打印为12,cons 1 cons 2 3生成的对象可以打印为12。三,

cons与atom的区别在语法中很重要,因为满足consp测试的表达式被视为复合形式。而不是关键字符号的原子,t或nil的计算结果是它们自己的

为了得到列表本身而不是复合表单的值,我们使用quote,我们有一个很好的速记

有一个向量类型是很有用的,它不会以这种方式纠缠在求值语义中:它的实例只是自求值原子

Cons细胞不是Lisps有载体之前的遗迹。首先,几乎没有这样的时间。1960年的LISP1手册已经描述了数组。第二,从那时起,新的方言仍然有其共同之处

具有相似表示的对象并不仅仅是相互冗余的。类型区分很重要。例如,我们不会认为以下两个彼此冗余,仅仅因为它们都有三个时隙:

(defstruct name first initial last)

(defstruct bank-transaction account type amount)
在TXR Lisp方言中,我曾经使用过它,因此语法糖a..b表示范围的cons a b。但这意味着范围是consp,这是愚蠢的,因为列表的模糊性。我最终修改了它,使a..b表示rcons a b:一种构造范围对象的形式,打印为Rx y。并且可以这样指定为文本。这产生了一个有用的细微差别,因为我们可以区分函数参数是范围范围参数还是列表参数。就像我们关心某个对象是银行交易还是名称一样。范围对象的表示方式与conse完全相同,并从相同的堆中分配;只是它们有一种不同的类型,使它们成为原子。如果作为形式进行评估,则它们会对自己进行评估

基本上,我们必须将类型视为一个额外的插槽。两个元素的向量实际上至少有三个属性,而不仅仅是两个:第一个元素、第二个元素和类型。向量1与cons单元1不同。因为它们都有第三个方面,类型,这是不同的

一个对象与其他同类对象共享的不可变属性仍然可以视为插槽。实际上,所有对象都有一个类型槽。因此conse实际上是三个属性对象,分别具有car、cdr和type:

她的位置是第四个位置:

(class-of '(a . b)) -> #<BUILT-IN-CLASS CONS>
我们不能仅仅从对象在堆上分配的每个实例存储向量的角度来看待对象


顺便说一句,1960年的MacLisp方言将cons的概念扩展到了固定大小的聚合对象中,除了car和cdr之外,这些对象还有更多的命名字段:cxr-s。这些物体被称为“大块头”,并且是。大块头不满足谓词consp,而是满足hunkp;i、 它们被认为是原子。然而,他们用多个点扩展了cons符号。

回答得很好。我只想为那些奇怪的noob加上一句,虽然type of会在cons单元格/列表中显示cons,但它们也可以使用typep和list。例如,typep'1 2 3'列表或typep cons 1 2'列表。typep'1 2 3'cons和typep cons 1 2'cons也会给出t。@tsikov typeof可能会给出cons,但它也会给出其他东西,例如t。有很大的灵活性。使用typep通常是更好的解决方案。Lisp1.5有数组。@RainerJoswig,第35页。我对1.5的功能集更感惊讶。
(class-of '(a . b)) -> #<BUILT-IN-CLASS CONS>