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)
有没有使用列表的例子,其中conj
和cons
performance出不同的行为,还是真的可以互换? 换句话说,有一个例子,列表和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