在有载体的Lisps中,为什么还需要cons细胞?根据我的理解,一个利弊细胞是:
所有这些也适用于2矢量。那有什么区别?在Lisps有载体之前,cons细胞是否只是一个遗迹?还是有其他我不知道的差异?
答案 0 :(得分:5)
虽然在物理上,conses类似于任何其他双元素聚合结构,但它们不仅仅是2矢量的过时形式。
首先,Lisp中的所有类型都被划分为cons
和atom
。只有类型为cons
;其他一切都是原子。矢量是一个原子!
Conses构成嵌套列表的表示基础,当然这些列表用于编写代码。它们有一个特殊的印刷符号,这样(cons 1 (cons 2 nil))
生成的对象可以方便地打印为(1 2)
,(cons 1 (cons 2 3))
生成的对象打印为(1 2 . 3)
。
cons
与atom
区别在语法中很重要,因为满足consp
测试的表达式被视为复合形式。而不是关键字符号的原子,t
或nil
会对自己进行评估。
要获取列表本身而不是复合形式的值,我们使用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实际上是具有car
,cdr
和type
的三个属性对象:
(car '(a . b)) -> A
(cdr '(a . b)) -> B
(type-of '(a . b)) -> CONS
她是第四个“插槽”:
(class-of '(a . b)) -> #<BUILT-IN-CLASS CONS>
我们无法仅根据堆上分配的每个实例存储向量来查看对象。
顺便说一下,1960年代的MacLisp方言将cons的概念扩展为具有更多命名字段的固定大小的聚合对象(除car
和cdr
之外):{{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标准指定类型vector
和cons
是不相交的,所以,事实上,你不能使用技巧实现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完整的
答案 4 :(得分:1)
在Lisps有载体之前,cons细胞是否只是遗留物?
完全。 cons
,car
,cdr
是第一个lisp中唯一复合数据结构的构造函数和访问器。为了区分它与唯一的原子类型符号,atom
符号为T
但cons
为假。这被扩展到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元素矢量类型来解决这个浪费,它的元素可以是任何东西。
或换句话说:通过重新引入利弊细胞。