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

我知道, cons返回一个seq和连接返回一个集合。 我也知道conj将这个物品“添加”到集合的最佳结尾,并且总是把这个物品“添加”到最前面。 这个例子说明了这两点:

 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) 

有没有使用列表的例子,其中conjconsperformance出不同的行为,还是真的可以互换? 换句话说,有一个例子,列表和seq不能等价使用吗?

一个区别是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 ,因此它不再是一个常量时间操作(在这种情况下,它可能会减less到1 + 3 – 1来自线性遍历第一个元素,3来自(next (cons 4 '(1 2 3))PersistentList ,因此计算)。

名字背后的意图是,我相信, cons意思是cons(truse seq) 1 ,而conj意味着连接(一个项目到一个集合)。 由cons构造的seq以作为其第一个parameter passing的元素开始,并且将seq应用于第二个参数的结果作为其next / rest部分。 如上所示,整个事情是类clojure.lang.Cons 。 相反, conj总是返回一个与传递给它的集合大致相同types的集合。 (粗略地说,因为PersistentHashMap只要超过9个条目就会变成一个PersistentHashMap 。)


1传统上,在Lisp世界中, cons cons(构成一对),因此Clojure从Lisp传统中脱离了cons函数构造一个没有传统cdr的seq。 cons的一般用法是指“构造某种types或者某种types的logging”,目前在编程语言及其实现的研究中无处不在; 这就是提到“回避”的意思。

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

你可以把连接想象成一个“插入某个地方”的操作,并且认为是一个“插入头部”的操作。 在列表中,插入头部是最合乎逻辑的,所以在这种情况下,conj和cons是等价的。

另一个区别是,因为conj将序列作为第一个参数,所以在将ref到某个序列时,它会很好地与alter进行比较:

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

这基本上是以线程安全的方式进行的(conj a-sequence-ref an-item) 。 这不会与cons 。 有关更多信息,请参阅Stu Halloway 编程Clojure中的并发一章。

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

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