为什么Clojure习惯用法更喜欢返回nil而不是像Scheme一样的空列表?
从中,有人说Clojure习惯用法更喜欢返回nil,而不是Scheme中的空列表。为什么呢 像 而不是为什么Clojure习惯用法更喜欢返回nil而不是像Scheme一样的空列表?,clojure,idioms,Clojure,Idioms,从中,有人说Clojure习惯用法更喜欢返回nil,而不是Scheme中的空列表。为什么呢 像 而不是 (if (empty? lat) '() ...) 既然我写了评论,我会写一个答案。(skuro的回答提供了所有信息,但可能太多) 首先,我认为更重要的事情应该放在第一位 seq正是每个人大部分时间都在使用的东西,但是empty?对它来说很好(不是(seq lat)) 在Clojure中,“()是true,因此通常情况下,如果序列完成,您希望返回false 如果您的数据库中只
(if (empty? lat)
'() ...)
既然我写了评论,我会写一个答案。(skuro的回答提供了所有信息,但可能太多)
seq
正是每个人大部分时间都在使用的东西,但是empty?
对它来说很好(不是(seq lat))
do
附言
并不是说有函数称为
如果不是和如果不是,它们通常比(如果不是真的)
更好。
我可以想到几个原因:
- 逻辑区别。在Clojure中,nil表示无价值/无价值。而“()”空列表是一个值-它恰好是一个空列表的值。从概念上和逻辑上区分这两者通常很有用
- 适合JVM-JVM对象模型支持空引用。相当多的Java API返回空表示“无”或“未找到值”。因此,为了确保易于JVM互操作性,Clojure以类似方式使用nil是有意义的
- 惰性-这里的逻辑相当复杂,但我的理解是,使用nil表示“no list”更适合Clojure。由于Clojure在默认情况下是一种惰性函数式编程语言,因此将此用法作为标准用法是有意义的。有关更多说明,请参阅
- “Falsiness”-在编写检查集合的条件代码时,可以方便地使用nil表示“nothing”,也可以表示“false”-因此您可以编写类似
(if(some map:some key)…)
的代码来测试hashmap是否包含给定键的值
- 性能-测试nil比检查列表是否为空更有效……因此,将此习惯用法作为标准可以产生更高性能的习惯用法代码
请注意,Clojure中仍有一些函数返回空列表。例如rest:
(rest [1])
=> ()
这详细说明了为什么会这样……还请注意,集合类型和nil的并集形成了一个幺半群,将幺半群加上,nil将幺半群为零。因此,nil将空列表语义保持在串联状态下,同时还表示一个假值或“缺失”值
Python是另一种语言,其中公共幺半群标识表示假值:0、空列表、空元组
由于空集合在布尔上下文中的行为类似于true
,因此您需要一种习惯用法来测试集合中是否有任何内容需要处理。谢天谢地,Clojure提供了这样一种技术:
(seq [1 2 3])
;=> (1 2 3)
(seq [])
;=> nil
在其他Lisp中,如Common Lisp,空列表用于表示nil
。这称为nil双关,只有在空列表为false时才可行。返回nil
这是clojure重新引入nil双关的方法。FWIW,如果返回()
作为一个文本,调用方可以快速测试它,因为它的类将是clojure.lang.PersistentList$EmptyList
。检查它肯定比测试empty?
或seq
要快,尽管可能仍然比检查nil
要慢一点。我想应该有助于解决这个问题。一个错误lue当然比truthy更适合返回。其他Lisp(如Common Lisp)将空列表视为虚假。Clojure没有按照skuro链接中的答案进行操作。如果(if)(seq some list)…
始终以第一种形式执行。Scheme通常不会返回空列表,Scheme过程通常会返回#f
或根本不返回值。空列表通常作为递归过程的基本情况返回。您知道如果有加法,任何n+0=n?,如果有逻辑or,任何n或true=n?,如果在Clojure中有列表连接,any(concat n nil)=n?因此,在操作中存在这种“缺失事物”的模式。Clojure使用nil表示许多情况下的“缺失”概念,因此您可以使常用算法(如树漫游器)更通用、更清晰。
(seq [1 2 3])
;=> (1 2 3)
(seq [])
;=> nil