Vector 在Clojure中,用什么惯用方法来前置向量?
在列表前加上前缀很容易:Vector 在Clojure中,用什么惯用方法来前置向量?,vector,clojure,append,prepend,Vector,Clojure,Append,Prepend,在列表前加上前缀很容易: user=> (conj '(:bar :baz) :foo) (:foo :bar :baz) 附加到向量很容易: user=> (conj [:bar :baz] :foo) [:bar :baz :foo] 如何(习惯用法)在返回向量的同时预加向量? 这不起作用,因为它返回的是序列,而不是向量: user=> (cons :foo [:bar :baz]) (:foo :bar :baz) 这很难看(IMVHO): 注意:我基本
user=> (conj '(:bar :baz) :foo)
(:foo :bar :baz)
附加到向量很容易:
user=> (conj [:bar :baz] :foo)
[:bar :baz :foo]
如何(习惯用法)在返回向量的同时预加向量?
这不起作用,因为它返回的是序列,而不是向量:
user=> (cons :foo [:bar :baz])
(:foo :bar :baz)
这很难看(IMVHO):
注意:我基本上只需要一个数据结构,我可以附加和前置到它。添加到大型列表会带来很大的性能损失,因此我想到了向量。向量不是为预加而设计的。您只有O(n)个前置词:
你想要的很可能是一个。我知道这个问题很老了,但没人说有什么不同 列表,既然你说你真的只是想要一些你可以附加的东西 在前面加上,听起来差异列表可能会对你有所帮助。 它们在Clojure似乎不受欢迎,但非常简单 要实现和手指树相比要简单得多,所以我做了一个 刚才(甚至测试了它)的列表库的微小差异。这些 在O(1)时间内串联(前置或追加)。转换差异 列表返回到一个列表应该花费O(n),如果 您正在进行大量的连接。如果你没有做很多 连接,然后只保留列表,对吗?:) 以下是这个小型库中的函数: dl:差异列表实际上是一个包含自己的函数 包含参数并返回结果列表。每一次 生成一个差异列表,创建一个小函数 其行为类似于数据结构 dlempty:因为差异列表只是将其内容与 参数,则空的差异列表与标识相同 功能 undl:由于差异列表的作用,您可以转换 只需使用nil调用它,就可以将列表与普通列表区别开来,所以 功能不是真正需要的;这只是为了方便 dlcons:将一个项目放在列表的前面,但不是全部 这是必要的,但考虑是一个很常见的操作,它只是一个 一行程序(与这里的所有函数一样) dlappend:包含两个不同的列表。我认为它的定义是 最有趣的——看看吧!:) 现在,这是一个很小的库——5个单行函数,给你一个O(1) 追加/前置数据结构。不错吧?啊,兰姆达的美丽 微积分
(defn-dl
“返回列表的差异列表”
[l]
(fn[x](concat l x)))
; 返回一个空的差异列表
(定义空标识)
(defn undl)
“返回差异列表的列表(只需使用nil调用差异列表即可)”
[aDl]
(每日无)
(defn-dlcons)
“将项目合并到差异列表”
[项目aDl]
(fn[x](反对项(aDl x)))
(defn-dlappend)
“附加两个差异列表”
[dl1 dl2]
(fn[x](dl1(dl2x)))
您可以通过以下方式看到它的作用:
(undl(dlappend(dl’(1233))(dl’(456)))
返回:
(123456)
这也会返回相同的内容:
((dl'(1234))(456))
享受差异列表的乐趣
更新
以下是一些可能更难理解的定义,但我认为更好:
(defn dl[&elements](fn[x](concat elements x)))
(定义为“无”)
(定义dl concat[&列表](fn[x]((应用comp列表)x)))
你可以这样说:
(dl-un(dl-concat(dl-1)(dl-23)(dl)(dl-4)))
哪个会回来
(1234)
正如用户optevo在手指树回答下的评论中所说,您可以使用实现RRB树的lib:
RRB树建立在Clojure的PersistentVectors之上,添加对数时间连接和切片。ClojureScript由相同的API支持,除了缺少函数的向量
我决定将此作为一个单独的答案发布,因为我认为这个图书馆值得这样做。它支持ClojureScript,由Clojure社区中非常有名的、致力于实现各种数据结构的ClojureScript维护。我建议使用便利功能。例如:
(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]
相比之下,使用raw Clojure很容易犯错误:
; Add to the end
(concat [1 2] 3) ;=> IllegalArgumentException
(cons [1 2] 3) ;=> IllegalArgumentException
(conj [1 2] 3) ;=> [1 2 3]
(conj [1 2] 3 4) ;=> [1 2 3 4]
; Add to the beginning
(conj 1 [2 3] ) ;=> ClassCastException
(concat 1 [2 3] ) ;=> IllegalArgumentException
(cons 1 [2 3] ) ;=> (1 2 3)
(cons 1 2 [3 4] ) ;=> ArityException
如果您不害怕准量化,这个解决方案实际上相当优雅(对于“优雅”的一些定义):
实际上,我有时会在实际代码中使用它,因为声明性语法使它非常可读。rrb向量可以在O(logn)时间内串联(因此是前置的)。看,我也推荐rrb向量。我找到了这篇文章,在挖掘Clojure邮件列表后,我找到了另外两个lib,它们应该用来替换data.finger-tree并添加ClojureScript支持。rrb vector是一个由MichałMarczyk维护的contrib项目,MichałMarczyk是Clojure社区内受人尊敬的贡献者。并不是说其他LIB的作者不是,只是我——作为Clojure的新手——不认识他们。Clojure的Vector没有真正的理由不能以与追加相同的时间复杂度进行前置。我已经在Go中实现了这一点,这里:。解决方案是1)记住“起始偏移”(默认值=0)并在所有树操作中使用它。2) 在预结束时,如果“开始偏移量”=0,则分配一个新的根节点,将旧的根节点放置在其中间,并将“开始偏移量”更新到此位置。3) 在所有其他情况下,prepend只会变成减少“开始偏移量”并在索引0处执行正常关联的情况。只有当节点将存储数据时,才延迟初始化节点,从而消除该设计中对内存浪费的担忧。通过增加“起始偏移量”并在第0个元素上执行空的assoc,可以实现shift
操作(左侧的pop)。必须注意删除索引遵循的节点路径<“开始偏移量”将需要。我还没有研究它,但我
(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]
; Add to the end
(concat [1 2] 3) ;=> IllegalArgumentException
(cons [1 2] 3) ;=> IllegalArgumentException
(conj [1 2] 3) ;=> [1 2 3]
(conj [1 2] 3 4) ;=> [1 2 3 4]
; Add to the beginning
(conj 1 [2 3] ) ;=> ClassCastException
(concat 1 [2 3] ) ;=> IllegalArgumentException
(cons 1 [2 3] ) ;=> (1 2 3)
(cons 1 2 [3 4] ) ;=> ArityException
> `[~:foo ~@[:bar :baz]]
[:foo :bar :baz]