cons细胞和2载体之间有什么区别?

时间:2016-10-26 11:54:09

标签: lisp common-lisp

在有载体的Lisps中,为什么还需要cons细胞?根据我的理解,一个利弊细胞是:

  • 具有2个元素的结构
  • 访问权限为O(1)

所有这些也适用于2矢量。那有什么区别?在Lisps有载体之前,cons细胞是否只是一个遗迹?还是有其他我不知道的差异?

6 个答案:

答案 0 :(得分:5)

虽然在物理上,conses类似于任何其他双元素聚合结构,但它们不仅仅是2矢量的过时形式。

首先,Lisp中的所有类型都被划分为consatom。只有类型为cons;其他一切都是原子。矢量是一个原子!

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

consatom区别在语法中很重要,因为满足consp测试的表达式被视为复合形式。而不是关键字符号的原子,tnil会对自己进行评估。

要获取列表本身而不是复合形式的值,我们使用quote,我们有一个很好的速记。

有一个矢量类型没有被这种方式纠缠到评估语义中是有用的:它的实例只是自我评估的原子。

在Lisps有载体之前,缺陷细胞不是遗骸。首先,几乎没有这样的时间。 1960年的Lisp 1手册已经描述了数组。其次,从那时起,新的方言仍然有用。

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

(defstruct name first initial last)

(defstruct bank-transaction account type amount)

在TXR Lisp方言中,我曾经拥有它,因此语法糖a..b表示范围(cons a b)。但这意味着范围是consp,由于对列表的模糊性,这是愚蠢的。我最终更改了它,以便a..b表示(rcons a b):构造范围对象的表单,其打印为#R(x y)。 (并且可以指定为文字)。这会产生一个有用的细微差别,因为我们可以区分函数参数是范围(rangep)还是列表(consp)。就像我们关心某个对象是bank-transaction还是name一样。范围对象的表示与conses完全相同,并从相同的堆分配;只是他们有不同的类型,使他们成为原子。如果评估为表格,他们会评估自己。

基本上,我们必须将类型看作是一个额外的插槽。两元素向量确实具有(至少)三个属性,而不仅仅是两个:它具有第一个元素,第二个元素和一个类型。向量#(1 1)与cons单元格(1 . 1)的不同之处在于它们都具有第三个方面,类型,它不相同。

与所有其他同类对象共享的对象的不可变属性仍可视为“插槽”。实际上,所有对象都有一个“类型槽”。因此,conses实际上是具有carcdrtype的三个属性对象:

(car '(a . b)) -> A
(cdr '(a . b)) -> B
(type-of '(a . b)) -> CONS

她是第四个“插槽”:

(class-of '(a . b)) -> #<BUILT-IN-CLASS CONS>

我们无法仅根据堆上分配的每个实例存储向量来查看对象。

顺便说一下,1960年代的MacLisp方言将cons的概念扩展为具有更多命名字段的固定大小的聚合对象(除carcdr之外):{{1} } -s。这些对象被称为“帅哥”并且是documented in Kent Pitman's MacLisp manual。 Hunks不满足谓词cxr,但是consp;即它们被认为是原子。但是,他们用多个点扩展了这个缺点。

答案 1 :(得分:2)

在典型的Common Lisp实现中,cons单元将表示为“两个机器字”(一个用于汽车指针,一个用于cdr指针;事实上,它是一个cons单元,在构造为引用的指针中编码它)。但是,数组是更复杂的对象,除非你有一个专用的“只有两个元素的T型向量”,你最终会得到一个包含类型信息和大小的数组头,以及存储元素所需的存储空间。 (可能很难挤到“四机词”之下)。

因此,尽管使用双元素向量/数组作为cons单元显然是可能的,但是基于cons单元和列表在现有Lisp中经常使用的事实,使用专用类型可以获得一些效率。代码。

答案 2 :(得分:1)

一方面,这是一个“实现细节”:给定向量,可以使用长度为2的向量实现cons单元(以及链接列表)。

另一方面,这是一个相当重要的细节:ANSI Common Lisp标准指定类型vectorcons是不相交的,所以,事实上,你不能使用技巧实现ANSI CL。

答案 3 :(得分:1)

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

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

CL-USER> (type-of (list 1 2 3))
CONS

这是一个CONS,而且会议上有类似的内容(看看常见的lisp hypersec):

  

列表n。         1.每个利弊的汽车是列表中的一个元素的一系列同义词,每个利弊的cdr是其中的下一个链接。   链或终止原子。另请参阅正确的列表,点列表或   循环清单。         2. null和cons联合的类型。

因此,如果使用向量而不是缺点创建Lisp,则不是ANSI CL

所以你可以创建列表&#34; consing&#34;东西,零是一个列表,你可以用consing创建不同类型的列表:

通常你会创建一个合适的列表:

(list 1 2 3) = (cons 1 (cons 2 (cons 3 nil)))) = '(1 2 3)

当列表没有以nil结尾时,它是一个虚线列表,圆形列表有一个对它自己的引用

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

CL-USER> (type-of "this is a string")
(SIMPLE-ARRAY CHARACTER (16))

Land of lisp(一本关于常见lisp的好书)将cons定义为构建常见lisp和处理列表的粘合剂,因此当然如果用其他类似的东西替换cons,你将构建类似于常见的lisp的东西。 / p>

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

enter image description here

答案 4 :(得分:1)

  

在Lisps有载体之前,cons细胞是否只是遗留物?

完全。 conscarcdr是第一个lisp中唯一复合数据结构的构造函数和访问器。为了区分它与唯一的原子类型符号,atom符号为Tcons为假。这被扩展到Lisp 1.5中的其他类型,包括向量(称为数组,请参见第35页)。 Common Lisp是商业lisps的组合,都是基于lisp 1.5构建的。如果两种类型都是从一开始就制作的话,也许它们会有所不同。

如果您要进行Common Lisp实现,只要您的实现按照规范工作,您就不需要有两种不同的方法来实现它们。如果我没记错的话,我认为racket实际上用vector实现了struct,vector?被重载为#f,用于确实代表对象的向量。在CL中,您可以以相同的方式实现defstruct并实现cons struct以及与hyperspec兼容所需的函数。在您最喜欢的实现中创建cons时,您可能正在使用向量而不知道它。

从历史上看,你仍然拥有旧的功能,因此约翰麦卡锡的代码在第一次口齿不清的58年后仍然有效。它并不需要,但在现有语言正在使用的语言中获得一点遗产并没有什么坏处。

答案 5 :(得分:1)

如果你使用了双元素向量,你可以将它们的大小(和类型)存储在列表的每个节点中。
这太荒谬了。

你可以通过引入一个特殊的2元素矢量类型来解决这个浪费,它的元素可以是任何东西。

或换句话说:通过重新引入利弊细胞。