Clojure: cons (seq) vs. conj (list)
Posted
技术标签:
【中文标题】Clojure: cons (seq) vs. conj (list)【英文标题】: 【发布时间】:2011-03-01 19:21:44 【问题描述】:我知道cons
返回一个序列,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
表现出不同的行为,或者它们是否真的可以互换?换个说法,有没有一个 list 和 seq 不能等价使用的例子?
【问题讨论】:
【参考方案1】:一个区别是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
意味着 cons(truct a seq)1,而 conj
意味着 conj(将一个项目放入一个集合中)。由cons
构造的seq
以作为其第一个参数传递的元素开始,并将next
/ rest
部分作为将seq
应用于第二个参数的结果;如上所示,整个事物属于clojure.lang.Cons
类。相反,conj
总是返回一个与传递给它的集合大致相同类型的集合。 (粗略地说,因为PersistentArrayMap
将在超过 9 个条目后立即变成PersistentHashMap
。)
1 传统上,在 Lisp 世界中,cons
cons(tructs a pair),因此 Clojure 背离了 Lisp 传统,它的 cons
函数构造了一个没有一个传统的cdr
。 cons
的广义用法表示“构建某种类型的记录以将多个值保存在一起”目前在编程语言及其实现的研究中无处不在。这就是提到“避免欺骗”时的意思。
【讨论】:
写得真棒!我不知道有一个 Cons 类型。干得好! 谢谢。高兴听到。 :-) 顺便说一下,作为一种特殊情况,(cons foo nil)
返回一个单例PersistentList
(同样适用于conj
)。
另一个极好的解释。你真的是一个clojure绝地!
根据我的经验,当性能很重要时,将列表视为列表而不是序列是很重要的。【参考方案2】:
我的理解是,你说的是真的:列表上的 conj 等同于列表上的 cons。
您可以将 conj 视为“插入某处”操作,将 cons 视为“插入头部”操作。在列表中,最符合逻辑的做法是在头部插入,因此 conj 和 cons 在这种情况下是等价的。
【讨论】:
【参考方案3】:另一个区别是,因为conj
将序列作为第一个参数,所以在将ref
更新为某个序列时,它可以很好地与alter
配合使用:
(dosync (alter a-sequence-ref conj an-item))
这基本上以线程安全的方式执行(conj a-sequence-ref an-item)
。这不适用于cons
。有关更多信息,请参阅 Stu Halloway 的 Programming Clojure 中关于并发的章节。
【讨论】:
【参考方案4】:另一个区别是列表的行为?
(list? (conj () 1)) ;=> true
(list? (cons 1 ())) ; => false
【讨论】:
cons 总是返回一个序列,其中 conj 返回与提供的序列相同的类型【参考方案5】:有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]
【讨论】:
以上是关于Clojure: cons (seq) vs. conj (list)的主要内容,如果未能解决你的问题,请参考以下文章
如何具体化 Prolog 的回溯状态以执行与 Clojure 中的“lazy seq”相同的任务?