从对另一个问题的评论中,有人说 Clojure 习语更喜欢返回 nil 而不是像 Scheme 中那样返回空列表。为什么?
喜欢
(when (seq lat) ...)
而不是
(if (empty? lat)
'() ...)
我能想到几个原因:
-
逻辑上的区别。在Clojure中,nil意味着什么都没有/没有价值。而'()"空列表是一个值 - 它恰好是一个空列表的值。区分这两者通常在概念上和逻辑上都是有用的。
-
适合 JVM - JVM 对象模型支持空引用。相当多的Java API返回null表示"无"或"未找到值"。因此,为了确保JVM的互操作性,Clojure以类似的方式使用nil是有意义的。
-
懒惰 - 这里的逻辑相当复杂,但我的理解是,使用 nil 表示"无列表"更适合 Clojure 的懒惰序列。由于Clojure默认是一种惰性的函数式编程语言,因此将这种用法作为标准用法是有意义的。有关一些额外的说明,请参阅 http://clojure.org/lazy。
-
"虚假" - 在编写检查集合的条件代码时,使用 nil 表示"无"和表示"false"很方便 - 因此您可以编写类似
(if (some-map :some-key) ....)
的代码来测试哈希映射是否包含给定键的值。 -
性能 - 测试 nil 比检查列表以查看它是否为空更有效...因此,采用这个成语作为标准可以带来更高性能的惯用语代码
请注意,Clojure 中仍然有一些函数会返回一个空列表。一个例子是休息:
(rest [1])
=> ()
这个关于休息与下一个的问题详细介绍了为什么会这样......
另请注意,集合类型和 nil 的并集形成一个幺半群,幺半群加上 nil 幺半群零。因此,nil 将空列表语义保持在串联下,同时还表示一个假值或"缺失"值。
Python 是另一种语言,其中常见的幺半群恒等式表示假值:0、空列表、空元组。
摘自《The Joy of Clojure》
由于空集合的作用类似于布尔上下文中的
true
,因此需要一个习惯用法来测试集合中是否有要处理的内容。值得庆幸的是,Clojure提供了这样的技术:(seq [1 2 3]) ;=> (1 2 3) (seq []) ;=> nil
在其他 Lisp 中,如 Common Lisp,空列表用于表示 nil
。这称为 nil 双关语,只有在空列表为假时才可行。回到这里nil
是clojure重新引入零双关语的方式。
既然我写了评论,我就写一个答案。(skuro的答案提供了所有信息,但可能太多了)
- 首先,我认为更多的进口应该放在第一位。
-
seq
只是每个人大部分时间都在使用的东西,但empty?
只是(not (seq lat))
- 在 Clojure 中,'() 是真的,所以如果序列完成,你想返回一些假的东西。
- 如果你只有一个importend分支,如果另一个返回false/'()或类似的东西,为什么要写下那个分支。
when
只有一个分支,如果你想有副作用,这是特别好的。您不必使用do
.
请参阅此示例:
(如果为假'()(do (println 1) (打印ln 2) (println 3)))
你可以写
(当为真时 (打印ln 1) (打印ln 2) (印刷品3))
没有那么不同,但我认为最好阅读。
附言
并不是说有称为if-not
的函数,when-not
它们通常比(if (not true) ...)
更好