Lisp中的空列表是否由cons单元构建?

时间:2013-05-17 09:52:40

标签: lisp null cons

我试图在JavaScript中模拟类似Lisp的列表(只是一个没有实际原因的练习),但我正在努力弄清楚如何最好地表示一个空列表。

空列表只是一个nil值,还是存放在一个缺点单元格中?

我可以:

(car '())
NIL
(cdr '())
NIL

但肯定的空列表不能是(cons nil nil),因为它与存储单个nil的列表无法区分。它需要存储一些其他特殊值。

另一方面,如果没有从cons单元构建空列表,则似乎不可能有一致的高级接口用于将单个值附加到现有列表。功能如:

(defun append-value (list value) ...

会修改它的参数,但前提是它不是一个空列表,看起来很难看。

4 个答案:

答案 0 :(得分:12)

空列表只是nil符号(根据定义,符号不是conses)。 carcdr定义为nil,如果给定nil,则会返回nil

对于列表变异函数,它们返回一个您应该重新分配给变量的值。例如,查看nreverse函数的规范:它可以修改给定的列表,也可以不修改给定的列表,并且您应该使用返回值,而不是依赖它来进行就地修改。

即使是nconc,典型的破坏性附加函数,也是这样的:它的返回值是你应该使用的附加列表。指定来修改给定列表(最后一个除外),但是如果你给它{{1}}作为第一个参数,它就不能很好地修改它,所以你仍然必须使用返回值。

答案 1 :(得分:9)

信不信由你,这实际上是一个宗教问题。

人们敢于将某些方言称为某种Lisp,其中空列表是某种形式,或者是某种类型的聚合对象,而不仅仅是nil这样的原子。

例如,在" MatzLisp" (更好地称为Ruby)列表实际上是数组。

在NewLisp中,列表是容器:列表类型的对象,其中包含项的链接列表,因此空列表是空容器。 [Reference]

在Lisp语言中,这种类型的群集方法不是很多,空列表是原子,非空列表是二进制单元格,其中一个字段保存第一个项目,另一个字段保存其余部分列表。列表可以共享后缀。给定(1 2 3)之类的列表,我们可以使用cons创建(a 1 2 3)(b c 1 2 3)两者,共享(1 2 3)的存储空间。

(在ANSI Common Lisp中,空列表原子()与符号nil是同一个对象,它对自身进行求值并且也用作布尔值false。在Scheme中,() isn' ta符号,与Boolean false #f对象不同。但是Scheme列表仍然由对组成,并以原子终止。)

评估(car nil)的能力不会自动遵循列表的缺点和表示,如果我们查看古代Lisp文档,例如1960年初的Lisp 1.5手册,我们将会发现这是不存在的。最初,car严格来说是一种访问cons单元字段的方法,并且严格要求cons单元参数。

允许(car nil)正常工作(以便黑客可以从他们的程序中删除许多无用的代码行)的好主意并不是一夜之间出现的。允许(car nil)的想法可能来自InterLisp。在任何情况下, Evolution of Lisp 论文声称MacLisp(Common Lisp的重要前身之一,与二十年后出现的Apple Macintosh无关),模仿InterLisp的这一特性(另一个重要的前辈)。

这样的细节之间的区别在于令人愉快的编程和在监视器上发誓:例如A Short Ballad Dedicated to the Growth of Programs的灵感来自一位Lisp程序员与bletcherous方言的斗争,其中使用{无法访问空列表{1}},并且不作为布尔值假。

答案 2 :(得分:4)

NIL在Common Lisp中有点奇怪,因为

  • 这是一个符号(意味着symbolp返回T
  • 是一个列表
  • 不是一个缺点单元格(consp返回NIL
  • 无论如何,你可以CARCDR

请注意,这背后的原因可能也是历史性的,您不应该认为这是唯一合理的解决方案。其他Lisp方言做出了不同的选择。

答案 3 :(得分:2)

尝试使用Lisp解释器:

(eq nil '())
=> t

nil /空列表上操作时,有几个操作是特殊的,可以做非正交(甚至好奇:-)的事情。您正在调查的carcdr行为就是其中之一。

nil作为空列表的标识是您了解Lisp的第一件事。我试图想出一个好的谷歌热门,但我会选择一个,因为有这么多:http://www.cs.sfu.ca/CourseCentral/310/pwfong/Lisp/1/tutorial1.html