Clojure:cons (seq) 与 conj (list)
我知道 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 不能等效使用的例子?
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
一个区别是
conj
接受任意数量的参数插入到集合中,而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, whilecons
takes just one:Another difference is in the class of the return value:
Note that these are not really interchangeable; in particular,
clojure.lang.Cons
does not implementclojure.lang.Counted
, so acount
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 aPersistentList
and thusCounted
).The intention behind the names is, I believe, that
cons
means to cons(truct a seq)1, whereasconj
means to conj(oin an item onto a collection). Theseq
being constructed bycons
starts with the element passed as its first argument and has as itsnext
/rest
part the thing resulting from the application ofseq
to the second argument; as displayed above, the whole thing is of classclojure.lang.Cons
. In contrast,conj
always returns a collection of roughly the same type as the collection passed to it. (Roughly, because aPersistentArrayMap
will be turned into aPersistentHashMap
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 itscons
function construct a seq which doesn't have a traditionalcdr
. The generalised usage ofcons
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.我的理解是,你说的是真的:列表上的 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.
另一个区别是,由于
conj
将序列作为第一个参数,因此在将ref
更新为某个序列时,它可以与alter
很好地配合:这基本上是以线程安全的方式执行
(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 withalter
when updating aref
to some sequence:This basically does
(conj a-sequence-ref an-item)
in a thread-safe manner. This wouldn't work withcons
. See the chapter on Concurrency in Programming Clojure by Stu Halloway for more info.另一个区别是列表的行为?
Another difference is the behavior of list?
有专用函数Tupelo 库 将附加值或前置值添加到任何顺序集合:
There are dedicated functions in the Tupelo Library to add append or prepend values to any sequential collection: