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