Clojure:cons (seq) 与 conj (list)

发布于 2024-09-05 04:08:51 字数 540 浏览 3 评论 0原文

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

是否有任何使用列表的示例,其中 conjcons 表现出不同的行为,或者它们真的可以互换吗?换句话说,是否有一个列表和 seq 不能等效使用的例子?

I know that cons returns a seq and conj returns a collection. I also know that conj "adds" the item to the optimal end of the collection, and cons always "adds" the item to the front. This example illustrates both of these points:

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)

For vectors, maps, and sets these differences make sense to me. However, for lists they seem identical.

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)

Are there any examples using lists where conj vs. cons exhibit different behaviors, or are they truly interchangeable? Phrased differently, is there an example where a list and a seq cannot be used equivalently?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

情痴 2024-09-12 04:08:51

一个区别是 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)) 是 。

我相信这些名称背后的意图是 cons(truct a seq) 1,而 conj 表示 conj(将一个项目加入到集合中)。由 cons 构造的 seq 从元素作为其第一个参数传递,并具有将 seq 应用到第二个参数的结果作为其 next / rest 部分;上面,整个内容都是类 clojure.lang.Cons 的,相反,conj 总是返回一个与传递给它的集合类型大致相同的集合。 (粗略地说,因为 PersistentArrayMap 一旦超过 9 个条目,就会变成 PersistentHashMap。)


1 传统上,在 Lisp 世界中, cons cons(tructs apair),因此 Clojure 背离了 Lisp 传统,让其 cons 函数构造一个没有传统 cdr< 的 seq /代码>。 cons 的一般用法是指“构造某种类型或其他类型的记录以将多个值保存在一起”,目前在编程语言及其实现的研究中普遍存在;这就是提到“避免欺骗”时的含义。

One difference is that conj accepts any number of arguments to insert into a collection, while cons takes just one:

(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

Another difference is in the class of the return value:

(class (conj '(1 2 3) 4))
; => clojure.lang.PersistentList

(class (cons 4 '(1 2 3))
; => clojure.lang.Cons

Note that these are not really interchangeable; in particular, clojure.lang.Cons does not implement clojure.lang.Counted, so a count on it is no longer a constant time operation (in this case it would probably reduce to 1 + 3 -- the 1 comes from linear traversal over the first element, the 3 comes from (next (cons 4 '(1 2 3)) being a PersistentList and thus Counted).

The intention behind the names is, I believe, that cons means to cons(truct a seq)1, whereas conj means to conj(oin an item onto a collection). The seq being constructed by cons starts with the element passed as its first argument and has as its next / rest part the thing resulting from the application of seq to the second argument; as displayed above, the whole thing is of class clojure.lang.Cons. In contrast, conj always returns a collection of roughly the same type as the collection passed to it. (Roughly, because a PersistentArrayMap will be turned into a PersistentHashMap as soon as it grows beyond 9 entries.)


1 Traditionally, in the Lisp world, cons cons(tructs a pair), so Clojure departs from the Lisp tradition in having its cons function construct a seq which doesn't have a traditional cdr. The generalised usage of cons to mean "construct a record of some type or other to hold a number of values together" is currently ubiquitous in the study of programming languages and their implementation; that's what's meant when "avoiding consing" is mentioned.

披肩女神 2024-09-12 04:08:51

我的理解是,你说的是真的:列表上的 conj 相当于列表上的 cons 。

您可以将 conj 视为“在某处插入”操作,将 cons 视为“在头部插入”操作。在列表上,在头部插入是最合乎逻辑的,因此 conj 和 cons 在这种情况下是等效的。

My understanding is that what you say is true: conj on a list is equivalent to cons on a list.

You can think of conj as being an "insert somewhere" operation, and cons as being an "insert at the head" operation. On a list, it is most logical to insert at the head, so conj and cons are equivalent in this case.

燃情 2024-09-12 04:08:51

另一个区别是,由于 conj 将序列作为第一个参数,因此在将 ref 更新为某个序列时,它可以与 alter 很好地配合:

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

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

Another difference is that because conj takes a sequence as the first argument, it plays nicely with alter when updating a ref to some sequence:

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

This basically does (conj a-sequence-ref an-item) in a thread-safe manner. This wouldn't work with cons. See the chapter on Concurrency in Programming Clojure by Stu Halloway for more info.

无敌元气妹 2024-09-12 04:08:51

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

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

Another difference is the behavior of list?

(list? (conj () 1)) ;=> true
(list? (cons 1 ())) ; => false
无人问我粥可暖 2024-09-12 04:08:51

专用函数Tupelo 库 将附加值或前置值添加到任何顺序集合:

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

There are dedicated functions in the Tupelo Library to add append or prepend values to any sequential collection:

(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]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文