Clojure:cons(seq)vs. conj(list)

时间:2010-06-09 17:50:33

标签: collections clojure sequence

我知道cons会返回一个seq而conj会返回一个集合。我也知道conj“将项目添加到集合的最佳末端,而cons始终将项目”添加“到前面。这个例子说明了这两点:

user=> (conj [1 2 3] 4) //returns a collection
[1 2 3 4]
user=> (cons 4 [1 2 3]) //returns a seq
(4 1 2 3)

对于矢量,地图和集合,这些差异对我来说很有意义。但是,对于列表,它们似乎完全相同。

user=> (conj (list 3 2 1) 4) //returns a list
(4 3 2 1)
user=> (cons 4 (list 3 2 1)) //returns a seq
(4 3 2 1)

是否有使用列表的示例conjcons表现出不同的行为,或者它们是否真的可以互换?换句话说,有一个例子,列表和seq不能等效地使用吗?

5 个答案:

答案 0 :(得分:146)

一个区别是conj接受任意数量的参数插入到集合中,而cons只接受一个:

(conj '(1 2 3) 4 5 6)
; => (6 5 4 1 2 3)

(cons 4 5 6 '(1 2 3))
; => IllegalArgumentException due to wrong arity

另一个区别在于返回值的类:

(class (conj '(1 2 3) 4))
; => clojure.lang.PersistentList

(class (cons 4 '(1 2 3))
; => clojure.lang.Cons

请注意,这些并不是真正可以互换的;特别是,clojure.lang.Cons没有实现clojure.lang.Counted,因此它上面的count不再是一个恒定的时间操作(在这种情况下,它可能会减少到1 + 3 - 1来自从第一个元素的线性遍历开始,3来自(next (cons 4 '(1 2 3)),因为PersistentList,因而Counted)。

我认为名称背后的意图是cons意味着利弊(取决于seq) 1 ,而conj意味着联合({1}}一个集合)。由seq构造的cons以作为其第一个参数传递的元素开头,并且next / rest部分由seq的应用程序产生到第二个论点;如上所示,整个事情是clojure.lang.Cons类。相反,conj总是返回与传递给它的集合大致相同类型的集合。 (粗略地说,因为PersistentArrayMap一旦超过9个条目就会变成PersistentHashMap。)


1 传统上,在Lisp世界中,cons缺点(结对一对),所以Clojure在使用cons函数构造一个seq时脱离了Lisp传统。没有传统的cdrcons的一般用法意味着“构建某种类型或其他类型的记录以保持一些价值在一起”,目前在编程语言及其实现的研究中无处不在;这就是提到“避免消费”的意思。

答案 1 :(得分:9)

我的理解是你所说的是真的:列表上的conj等同于列表中的缺点。

您可以将conj视为“插入某处”操作,并将其视为“插入头部”操作。在列表中,插入头部是最合乎逻辑的,因此在这种情况下,结合和缺点是等效的。

答案 2 :(得分:8)

另一个不同之处在于,因为conj将序列作为第一个参数,所以在将alter更新为某个序列时,它与ref很好地匹配:

(dosync (alter a-sequence-ref conj an-item))

这基本上以线程安全的方式执行(conj a-sequence-ref an-item)。这不适用于cons。有关详细信息,请参阅Stu Halloway的Programming Clojure中的并发章节。

答案 3 :(得分:2)

另一个区别是列表的行为?

(list? (conj () 1)) ;=> true
(list? (cons 1 ())) ; => false

答案 4 :(得分:-1)

dedicated functions in the Tupelo Library可以向任何顺序集合添加附加值或前置值:

(append [1 2] 3  )   ;=> [1 2 3  ]
(append [1 2] 3 4)   ;=> [1 2 3 4]

(prepend   3 [2 1])  ;=> [  3 2 1]
(prepend 4 3 [2 1])  ;=> [4 3 2 1]