(conj nil 1)
返回一个空的 list
,因为 Clojure 习惯上将 nil
和空列表看作是同样的概念。所以,在 nil
上连接就像是请求在空列表上连接。
(conj nil)
返回 nil
,因为 conj
也是一个 transducer 函数,而 conj
的单参数必须是一个恒等函数。因此,conj
的单参数是恒等函数,它将始终返回传递的参数原样,因此如果您传递 nil
,您将得到 nil
回传;如果您传递 :foo
,您将得到 :foo
回传: (conj :foo) ;=> :foo
当将conj
变为转换器时,也增加了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
应该默认为一个空 list
,因此 (conj nil 1) ;=> (1)
被设置为返回一个 list
。
我认为这个决定可能是由于 Clojure 最初大量复制了 Common Lisp 的风格。在 Common Lisp 中,没有空序列这一说,当一个序列为空时,就是 nil
。因此 nil
就是空序列。最初,Clojure 复制了这个风格,但后来逐渐与一些 Common Lisp 风格有所不同。
后来这种发展为将 conj
变为转换器,这意味着为它增加了 0-arity
和 1-arity
。转换器中的 1-arity
是 completion
函数,它说明在应用转换器结束时应该做什么,在 conj
的情况下,我们不需要做任何事情,除了返回结果,所以 conj
的 1-arity
只返回其参数,即它是一个恒等函数。
现在,转换器中的 0-arity
是 init
函数,当没有初始收集时会被调用,以决定默认值,并可以用来设置与特定转换器相关的某些事项。这里的问题是:在将 conj
用于没有指定起始集合的转换器时,应该有什么默认值?在这种情况下,决定应该是 vector
,我相信这只是一个简单的事实,因为 Clojure 如我以前所说,开始改变自己的风格,其中之一是从 Common Lisp 风格转变为自己的风格,这其中包括在 Clojure 中向量通常更有用,默认使用它们允许更直观的插入顺序,当处理一个序列作为向量时,你将按其原始顺序获取元素,这往往是更常需要的结果。