【问题标题】:Why do we need `nil`?为什么我们需要`nil`?
【发布时间】:2012-01-30 09:55:10
【问题描述】:

我不明白为什么我们需要nil [1] 当cons 一个项目序列(所谓的正确列表)。在我看来,我们可以通过单独使用所谓的不正确列表(cons-ed 没有结尾 nil)来实现相同的目标。由于 Lisps [2] 已经提供了一个原始过程来区分 pair? 和原子(一些实现甚至提供 atom?),当在列表上定义过程时,例如 length,我可以做同样的事情只有点对,如下所示:

(define len
  (lambda (l)
    (cond ((pair? l) (+ 1 (len (cdr l))))
          (else 1) ) ) )

很明显,与传统的(length '(1 2 3)) 相比,我们可以将此过程应用于'(1 . (2 . 3)) 等不正确的列表以获得预期的答案3

我想听听为nil 的必要性辩护的任何意见。提前致谢。

[1] 让我们忽略nil/NIL'()() 之间的争论。

[2] 这里指的是 Lisp 语言家族。

【问题讨论】:

  • 想想如果你有一个列表会发生什么?你就会明白为什么需要nil

标签: lisp scheme s-expression


【解决方案1】:

使用没有nil(或'())的列表就像在没有零的情况下进行算术一样。只使用没有nil 的对,我们将如何表示一个空列表或单例列表'(1)

更糟的是:由于列表不必是原子列表,但可以包含其他列表,我们将如何表示嵌套列表'(1 2 (3 4))?如果我们进行以下转换:

'(3 4) => '(3 . 4)
'(1 2 x) => '(1 . (2 . x)) == '(1 2 . x)

我们得到:

'(1 2 (3 4)) => '(1 . (2 . (3 . 4))) == '(1 2 3 . 4)

还有:

'(1 2 3 4) => '(1 . (2 . (3 . 4))) == '(1 2 3 . 4)

因此,仅使用对而不使用 nil 构造列表会阻止我们区分嵌套列表结构和平面列表,至少在列表的末尾是这样。您仍然可以将嵌套列表作为除最后一个之外的任何元素包含在内,因此现在对列表的元素可以是什么有一个奇怪且任意的限制。

从理论上讲,正确的列表是一种归纳定义的数据类型:列表或者是空列表,或者它有一个 first 元素,可以是任何东西,还有一个 rest,它总是 以相同方式定义的另一个列表。去掉空列表,现在您有了一个数据类型,其中rest 可能 是另一个列表,或者它可能是列表的最后一个元素。我们只能通过将其传递给pair? 来判断,这会导致上面的嵌套列表出现问题。保留nil 可以让我们拥有任何我们喜欢的列表元素,并允许我们区分1'(1)'((1)) 等等。

【讨论】:

  • 乔恩,感谢您的回答。你能给出一个单例有用的场景吗?谢谢。
  • 我认为这归结为拥有一致的界面。如果您有一个函数可能不返回任何项目、一个项目或多个项目,那么(至少)必须为一个项目的情况添加特殊代码是很烦人的。一个例子是用不明确的语法解析一个字符串:解析器可能会失败,返回'(),或者匹配一次,或者任意次数。此外,Lisp 的一个不错的部分是像 mapfilter 这样的函数,它们可以在 <something> 列表上工作,而无需知道其内部细节。如果没有适当列表的归纳定义,这些将更难编写。
  • 再澄清一点:假设我们更改了解析/匹配函数,以便在单匹配的情况下它只返回简单的结果,而不是单例列表。现在,该函数的每个用户都必须在对其执行任何其他操作之前对返回值进行额外的pair? 检查。 (这甚至可能行不通:如果解析器的结果本身就是列表怎么办?)对于几乎所有目的,单项列表本身并没有什么特别之处,所以这是额外的代码和努力,不会增加任何东西。
  • 没问题!希望对您有所帮助。
【解决方案2】:

您需要它来表示“无”。

【讨论】:

    猜你喜欢
    • 2019-06-09
    • 2014-06-18
    • 2017-02-26
    • 2011-04-03
    • 2017-07-27
    • 2020-09-21
    • 2020-03-09
    • 2018-12-24
    • 2012-04-08
    相关资源
    最近更新 更多