Collections 将元素放在集合的尾部

Collections 将元素放在集合的尾部,collections,clojure,Collections,Clojure,我发现自己做了很多: (concat coll[e])其中coll是集合,e是单个元素 在Clojure中有这样做的功能吗?我知道conj最适合向量,但我不知道会使用哪个coll。例如,它可以是向量、列表或排序集。某些类型的集合可以便宜地添加到前面(列表、序列),而其他类型的集合可以便宜地添加到后面(向量、队列、排序序列)。如果可能的话,你不应该使用concat,而应该安排使用其中一种类型(vector是最常见的)并只使用concat:(conj[1 2 3]4)产生[1 2 3 4],而(co

我发现自己做了很多:

(concat coll[e])
其中coll是集合,e是单个元素


在Clojure中有这样做的功能吗?我知道conj最适合向量,但我不知道会使用哪个coll。例如,它可以是向量、列表或排序集。

某些类型的集合可以便宜地添加到前面(列表、序列),而其他类型的集合可以便宜地添加到后面(向量、队列、排序序列)。如果可能的话,你不应该使用concat,而应该安排使用其中一种类型(vector是最常见的)并只使用concat:
(conj[1 2 3]4)
产生
[1 2 3 4]
,而
(conj'(1 2 3)4)
产生
(4 1 2 3)
concat不会在集合的尾部添加元素,它也不会连接两个集合

concat返回由两个其他seq串联而成的seq。对于concat的返回类型,可以从中推断seq的集合的原始类型将丢失

现在,clojure集合有不同的属性,为了编写高效的代码,必须了解这些属性,这就是为什么在core中没有一个通用函数可以将任何类型的集合连接在一起。
相反,列表和向量确实有conj知道的“自然插入位置”,并且做了适合这种收集的事情。

要从amalloy和Laurent Petit已经说过的话中提取最好的东西:使用
conj
函数

Clojure提供的一个伟大的抽象是Sequence API,它包括
conj
函数。如果可能的话,您的代码应该尽可能与集合类型无关,而不是使用seq API来处理集合上的操作,并仅在需要特定的时候选择特定的集合类型


如果向量匹配良好,则是,
conj
将在末尾添加项目。如果改为使用列表,那么
conj
将在您的收藏前面添加内容。但是,如果使用标准的seqapi函数从集合的“顶部”(向量的后面,列表的前面)提取项目,那么使用哪种实现并不重要,因为它总是使用性能最好的,因此添加和删除项目是一致的。

这是对@amalloy答案的一个非常小的补充,以满足OP对始终添加到任何类型集合尾部的功能的要求。这是
(concat coll[x])
的替代方案。只需创建原始集合的矢量版本:

(defn conj*
  [s x]
  (conj (vec s) x))
注意事项:

如果您从一个惰性序列开始,那么您现在已经破坏了惰性——也就是说,输出不是惰性的。这可能是好事,也可能是坏事,这取决于你的需要


创建向量需要一些成本。如果您需要多次调用此函数,并且您发现(例如,通过使用Criterium进行基准测试)此成本对于您的目的来说非常重要,然后按照其他答案的建议,首先尝试使用向量。

你会如何编写一个需要任何类型的coll才能完成的函数?两年后编辑:我不会写那个函数。@amalloy你能解释一下为什么不写那个函数吗(我想你说的是concat,而不是你原来的答案)?@Jon如果我需要在集合的末尾添加项目,我会:(a)安排一些事情,以便我可以使用向量或懒洋洋地构建序列,或者(b)通过强迫自己编写
(concat coll[x])来确保它看起来很难看
每一次,我都要确保元素被添加到集合的尾部。@Michiel Borkent做一个“我是否使用了该语言最惯用的构造”检查,这样做是不会有任何伤害的。如果没有更多细节(例如,为什么需要在结尾),很难进一步帮助。我不知道Michiel的情况,但我正在研究谓词参数的扩展。例如(defn x[[a b]])可以工作,但(defn x[(a b)]不能。对于元组顺序问题。在Scheme中,Lisp方言中内置了
append
。我认为从语言中期待这样的函数是完全合理的。“例如,它可能是一个……排序集。”允许调用者将项目放在尾部位置和保持排序顺序是相互排斥的。您对尾部位置的关注程度暗示了vector。那么(conj(vec coll)e)呢?在这种情况下,消除惰性并不是真正的成本。添加到列表末尾意味着列表将完全实现。