2010-06-09 46 views
89

我知道cons返回一个seq,而conj返回一个集合。我也知道conj会将该项目“添加”到该集合的最佳结尾,并且cons总是将该项目“添加”到前面。这个例子说明了这两点:Clojure:cons(seq)vs. conj(list)

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不能等价使用?

回答

140

一个区别是,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意味着利弊(truct一SEQ),而conj装置以连词(OIN项目到集合)。由cons构造的seq以作为其第一个参数传递的元素开始,并且具有作为其/rest部分的由于将seq应用于第二个参数而产生的部分;如上所示,整个事情是类clojure.lang.Cons。相反,conj始终返回与传递给它的集合大致相同类型的集合。 (粗略地说,因为一个PersistentArrayMap将尽快变成PersistentHashMap因为它的增长超出9项。)


传统上,在Lisp中世界,cons利弊(tructs一对),所以Clojure的离去从Lisp传统的cons函数构造一个没有传统cdr的seq。 cons的一般用法意指“构造某种类型的记录或将其保存在一起的多个值”,目前在编程语言及其实现的研究中无处不在;这就是当提到“避免顾虑”时的意思。

+1

这是多么美妙的文章!我不知道有一个Cons类型。做得好! – 2010-06-09 21:18:45

+0

谢谢。高兴听到。 :-) – 2010-06-09 21:25:32

+2

顺便说一句,作为一个特殊情况,'(cons foo nil)'返回一个单例PersistentList'(同样对'conj')。 – 2010-06-09 21:30:02

9

我的理解是你说的是真的:列表上的conj相当于列表上的缺点。

你可以认为conj是一个“插入某处”操作,并且认为是“插入头部”操作。在列表中,插入头部是最合理的,因此在这种情况下,conj和cons是等价的。

8

另一个不同之处是,由于conj接受一个序列作为第一个参数,它更新ref一些序列时alter起到很好:

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

这基本上不会在一个线程安全的方式(conj a-sequence-ref an-item)。这不适用于cons。请参阅Stu Halloway在Programming Clojure中有关并发的一章以获取更多信息。

1

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

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

cons总会返回一个连接,它会返回与所提供的类型相同的类型 – 2014-01-27 07:55:41