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

Collections Clojure:cons(seq)vs.conj(list),collections,clojure,sequence,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=> (c

我知道
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)

是否有使用列表的示例,其中
conj
cons
表现出不同的行为,或者它们是真正可以互换的?用不同的措词,是否有一个例子,列表和seq不能等价地使用?

我的理解是,你所说的是正确的:列表上的conj等同于列表上的cons


您可以将conj看作是一个“插入某处”操作,而cons则是一个“插入头部”操作。在列表中,最合乎逻辑的做法是在开头插入,因此在这种情况下conj和cons是等效的。

一个区别是
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
,因此它上面的
计数
不再是一个常数时间操作(在这种情况下,它可能会减少到1+3——1来自对第一个元素的线性遍历,3来自
(下一个(Cons 4'(1 2 3))
是一个
持久列表
,因此
计数

我相信,这些名字背后的意图是,
cons
意味着cons(构造顺序)1,而
conj
意味着conj(将一个项目连接到一个集合上)由
cons
构造的
seq
以作为其第一个参数传递的元素开始,并将
next
/
rest
作为其
seq
应用于第二个参数所产生的部分内容;如上所示,整个内容属于类
clojure.lang.cons
conj
始终返回与传递给它的集合类型大致相同的集合。(大致上,因为
PersistentArrayMap
一旦超过9个条目就会变成
PersistentHashMap



1传统上,在Lisp世界中,
cons
cons(构造一对),因此Clojure背离了Lisp的传统,让其
cons
函数构造一个没有传统
cdr
的seq“构造某种类型的记录以将多个值保存在一起”目前在编程语言及其实现的研究中无处不在;这就是“避免考虑另一个区别是,由于
conj
将序列作为第一个参数,因此在将
ref
更新为某个序列时,它与
alter
配合得很好:

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

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

另一个区别是列表的行为

(list? (conj () 1)) ;=> true
(list? (cons 1 ())) ; => false
可以向任何顺序集合添加附加值或前置值:

(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]

多么精彩的一篇文章!我不知道有Cons类型。干得好!谢谢。听到这个消息很高兴。:-)顺便说一句,作为特例,
(Cons foo nil)
返回一个单例
持久列表
(对于
conj
),这是另一个极好的解释。你真是一个clojure绝地武士!根据我的经验,当性能很重要时,将列表视为列表而不是序列很重要。cons总是返回一个序列,conj返回的序列与提供的序列类型相同