我试图在JavaScript中模拟类似Lisp的列表(只是一个没有实际原因的练习),但我正在努力弄清楚如何最好地表示一个空列表。
空列表只是一个nil
值,还是存放在一个缺点单元格中?
我可以:
(car '())
NIL
(cdr '())
NIL
但肯定的空列表不能是(cons nil nil)
,因为它与存储单个nil
的列表无法区分。它需要存储一些其他特殊值。
另一方面,如果没有从cons单元构建空列表,则似乎不可能有一致的高级接口用于将单个值附加到现有列表。功能如:
(defun append-value (list value) ...
会修改它的参数,但前提是它不是一个空列表,看起来很难看。
答案 0 :(得分:12)
空列表只是nil
符号(根据定义,符号不是conses)。 car
和cdr
定义为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
)CAR
和CDR
请注意,这背后的原因可能也是历史性的,您不应该认为这是唯一合理的解决方案。其他Lisp方言做出了不同的选择。
答案 3 :(得分:2)
尝试使用Lisp解释器:
(eq nil '())
=> t
在nil
/空列表上操作时,有几个操作是特殊的,可以做非正交(甚至好奇:-)的事情。您正在调查的car
和cdr
行为就是其中之一。
nil
作为空列表的标识是您了解Lisp的第一件事。我试图想出一个好的谷歌热门,但我会选择一个,因为有这么多:http://www.cs.sfu.ca/CourseCentral/310/pwfong/Lisp/1/tutorial1.html