我知道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不能等效地使用吗?
答案 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传统。没有传统的cdr
。 cons
的一般用法意味着“构建某种类型或其他类型的记录以保持一些价值在一起”,目前在编程语言及其实现的研究中无处不在;这就是提到“避免消费”的意思。
答案 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]