在 Scheme 中,`nil` 和 `null` 有什么区别?

In Scheme, what is the difference between `nil` and `null`?

在《小小阴谋家》中,null用来指代空表()。但我在 Scheme 的错误消息中也看到了称为 nil 的空列表,例如正在做:

(car ())

原因:

Error: Attempt to apply car on nil [car][1]

这听起来像是将 () 称为 nil


[1] 使用 replit.com 的 BiwaScheme 解释器版本 0.6.4


此题与What is the exact difference between null and null in common lisp类似。这是不同的,因为正如此处的答案所指出的,Common Lisp 和 Scheme 在处理 nilnull.

方面存在差异

不同 Lisp 之间的区别在于它们如何处理 true、false 和 nil。

在 Common Lisp 中,我们有 t 表示真,nil 表示假,尽管实际上除了 nil 之外的所有内容都是真的。如果列表为空,或者 find 什么也没找到,我们有 nil 可以直接测试。技术术语是 nil punning.

'() ; => NIL
(= 1 2) ; => NIL
(if '() "full" "empty") ; => "empty"

(find 5 '(1 2 3 4)) ; => NIL
(if (find 5 '(1 2 3 4)) "found" "not found" ; => "not found"

Clojure 的做法不同。有一个官方真值 true,还有两个假值,falsenil - 除了 falsenil 之外的所有值也都是真值。空列表不再是nil,空列表需要单独测试,有empty?。值 nil 更像是 C 中的 void。更好还是更差? - 你来决定。

'() ; => ()
(= 1 1) ; => true
(= 1 2) ; => false
(if '() "full" "empty") ; => "full"
(if (empty? '()) "empty" "full") ; => "empty"
(if nil "full" "empty") ; => "empty"
(if (println "Hello world") "full" "empty") ; => empty

Scheme 在意图上更接近 Clojure。这里有一个假值,#f。空列表不再是nil,空列表需要单独测试,有null?.

(= 1 1) ; => #t
(= 1 2) ; => #f
(null? '()) ; => #t
(null? 'nil) ; => #f

因此,null 是空列表的另一个名称,但 nil'nil 不是。

null ; => ()
nil  ; =>  cannot reference an identifier before its definition
'nil ; => nil

(car ())怎么样?您的方案 returns nil 来自评估 (),这就是 car 所抱怨的。我的Scheme,Racket博士,根本拒绝播放,说空表中缺少表达式。

我认为您在错误消息中看到的是历史产物。

在传统的 Lisp 语言中,nil 既是空列表又是布尔值 false,并且在其他方​​面相当特殊(例如,它是 也是 符号,这很奇怪)。

所以特别是 ()nil 在这样的 Lisp 中是同一个对象, (not nil) 是真的, (not '()) 也是真的(事实上(not ()) 既合法又正确)。空列表对象的谓词是 null(也许应该是 nullp,但历史表明它就是这样)并且 (null nil)(null '()) 和 [=20 一样为真=].

还有一些疑问 (car nil) aka (car '()) aka (car ()) 应该做什么.传统上答案是这样很好,(car nil)nil。如果你聪明的话,这会给你一个非常巧妙的 nil 实现。

Scheme 现在取消了所有这些双关语:

  • 有一个唯一的假对象是#f / #false,它不是空列表;
  • 所有其他对象都为真,因此 (not x) 仅在 x#f 时为真;
  • 有一个唯一的空列表对象,它是 (),它不是 #f,因此是 true;
  • 这个对象的谓词是null?,所以(null? '())是真的,但是(null? #f)是假的;
  • car()cdr是错误的;
  • nil只是一个符号,在语言中没有任何特殊地位。

我不认为 () 有一个名字是标准的,但显然给它一个名字可能会有用,null 很好:例如,Racket 就是这样做的。

所以在 Scheme 中,nil 消失了,空列表和布尔值 false 之间的双关语也随之消失。

但是很多实现Schemes的人都知道nil就是人们所说的空表。所以当他们写错误信息的时候,他们写的是 nil 而他们的意思是 (),因为很长一段时间以来每个人都这么叫 ()

我认为,这就是您在这里看到的。