(conj nil 1)
返回一个空列表,因为 nil
和空列表在 Clojure 中经常在概念上被视为相同。所以将 nil
连接到 list
就像请求连接到空列表。
(conj nil)
返回 nil
,因为 conj
也是一个转换函数,而 conj
转换器的 1-ary
必须是恒等函数。因此,conj
的 1-ary
是恒等函数,它始终仅返回传入的参数作为原样,所以如果你传入 nil
,你会得到 nil
返回,如果传入 :foo
,你会得到 :foo
返回:(conj :foo) ;=> :foo
(conj)
当将 conj
转换为变换器(transducer)时,还添加了 0-arity
,这是变换器的 init
属性。因此,在使用 transduce
时,向量可能是更好的默认值,因为大多数人会预期 (transduce (map inc) conj [1 2 3])
会返回 2 3 4
而不是 4 3 2
最初 conj
只有一种 2-arity
形式:如 (conj nil 1)
或 (conj [1 2] 3)
或 (conj '() 1)
。这是在变换器技术出现之前的时期,而 conj
仅仅是一个集合函数。从那个角度看,问题是,(conj nil 1)
应该返回什么?合理的回答是我们可能会将 nil
视为空列表,因此 nil
应默认为空列表,所以 (conj nil 1) ;=> (1)
被定义为返回列表。
我认为这个决定是因为 Cloujure 最初大量借鉴了 Common Lisp 的惯用来表达的方式。在 Common Lisp 中,没有空列表这样的东西,当一个列表为空时,它是 nil
。因此,nil
就是空列表。最初,Clojure 模仿了这个习惯,但后来逐渐开始从一些 Common Lisp 的习惯中分离出来。
这种后来的演变是 conj
也变为一个变换器,这意味着它添加了 0-arity
和 1-arity
。变换器中的 1-ary
是 completion
函数,它说明了应用变换器结束时的操作,对于 conj
来说,我们不需要做任何事,只是返回结果,因此 conj
的 1-ary
只是简单地返回其参数,即它是一个恒等函数。
在变换器中,现在 0-ary
是 init
函数,它将在没有起始集合时被调用,以决定默认值,也可以用它来设置特定变换器相关的一些功能性。现在这里的问题是:当在变换器中使用没有指定起始集合的 conj
时,默认值应该是什么?在这种情况下,决定是使用向量,我相信这仅仅是因为正如我之前所说的,随着 Cloujure 的发展,它开始改变其某些习惯,从 Common Lisp 中分离出来,其中之一就是在 Cloujure 中向量通常更有用,默认使用它们允许更直观的插入顺序,因此当你将集合作为向量进行操作时,你可以按照原始顺序获得元素,这正是你通常需要的。