在Lisp中由cons单元构建的空列表



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

是一个空列表只是一个nil值或它是在引擎盖下存储在一个cons单元格?

我可以:

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

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

另一方面,如果空列表不是从cons单元格构建的,则似乎不可能有一个一致的高级接口来向现有列表添加单个值。如下函数:

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

将修改它的实参,但前提是它不是空列表,这看起来很难看。

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

有一些方言,人们敢于称之为某种Lisp,在这些方言中,空表是某种集合对象,而不仅仅是像nil那样的原子。

例如,在"MatzLisp"(即Ruby)中,列表实际上是数组。

在NewLisp中,列表是容器:列表类型的对象包含项目的链表,因此空列表是空容器。[引用].

在Lisp语言中,没有这种引人注目的集群混乱,空列表是原子,非空列表是二元单元格,其中一个字段保存第一个项目,另一个字段保存列表的其余部分。列表可以共享后缀。给定一个像(1 2 3)这样的列表,我们可以使用cons创建(a 1 2 3)(b c 1 2 3),它们共享(1 2 3)的存储空间。

(在ANSI Common Lisp中,空列表原子()与符号nil是相同的对象,其计算结果为自身,并作为布尔值false。在Scheme中,()不是一个符号,并且与布尔值false #f对象不同。然而Scheme列表仍然由对组成,并以一个原子结束。)

计算(car nil)的能力不会自动从列表的cons和nil表示中得到,如果我们看一下古老的Lisp文档,比如1960年代早期的Lisp 1.5手册,我们会发现这是不存在的。最初,car严格地是访问cons单元格字段的一种方式,并且严格地需要一个cons单元格参数。

像允许(car nil)只工作这样的好主意(这样黑客就可以从他们的程序中删减许多无用的代码行)不是一夜之间出现的。允许(car nil)的想法可能来自InterLisp。无论如何, Lisp的演变论文声称MacLisp (Common Lisp的重要前身之一,与20年后出现的Apple Macintosh无关)模仿了InterLisp(另一个重要的前身)的这一特性。

像这样的小细节可以区分愉快的编程和对监视器的咒骂:例如,参考一个Lisp程序员与一种令人讨厌的方言的斗争所启发的《献给程序成长的短谣》,在这种方言中,空列表不能用car访问,也不能作为布尔值false。

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

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

即使是典型的析构式追加函数nconc也是这样工作的:它的返回值是你应该使用的追加列表。它被指定为就地修改给定列表(最后一个列表除外),但是如果您将nil作为第一个参数给它,它就不能很好地修改它,因此您仍然必须使用返回值。

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

  • 它是一个符号(意思是symbolp返回T)
  • 是一个列表
  • 不是cons单元格(consp返回NIL)
  • 你可以拿走CARCDR

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

用你的Lisp解释器试试:

(eq nil '())
=> t

当对nil/空列表进行操作时,有几个操作是特殊的,以执行不正交(甚至奇怪的:-)操作。你正在调查的carcdr的行为就是其中之一。

nil作为空列表的身份是您学习Lisp的第一件事。我试着找一个好的Google搜索结果但我只选一个,因为有太多了http://www.cs.sfu.ca/CourseCentral/310/pwfong/Lisp/1/tutorial1.html

相关内容

  • 没有找到相关文章

最新更新