在Clojure中预先添加到矢量的惯用方法是什么?

0x8*_*x89 54 vector append clojure prepend

在列表前面很容易:

user=> (conj '(:bar :baz) :foo)
(:foo :bar :baz)
Run Code Online (Sandbox Code Playgroud)

附加到矢量很简单:

user=> (conj [:bar :baz] :foo) 
[:bar :baz :foo]
Run Code Online (Sandbox Code Playgroud)

在获取向量时,我如何(惯用)前置到向量?这不起作用,因为它返回seq,而不是向量:

user=> (cons :foo [:bar :baz])     
(:foo :bar :baz)
Run Code Online (Sandbox Code Playgroud)

这很难看(IMVHO):

user=> (apply vector (cons :foo [:bar :baz])) 
[:foo :bar :baz]
Run Code Online (Sandbox Code Playgroud)

注意:我基本上只想要一个可以附加和前置的数据结构.附加到大型列表应该有很大的性能损失,所以我想到了矢量..

kot*_*rak 71

矢量不是为前置而设计的.你只有O(n)前置:

user=> (into [:foo] [:bar :baz])
[:foo :bar :baz]
Run Code Online (Sandbox Code Playgroud)

你想要的很可能是手指树.

  • rrb-vectors可以在O(log n)时间内连接(因此预先插入).请参阅https://github.com/clojure/core.rrb-vector (2认同)
  • 我也推荐 rrb-vectors。我找到了这篇文章,在深入研究 Clojure 邮件列表后,我发现了另外两个库,它们应该替换 data.finger-tree 并添加 ClojureScript 支持。rrb-vector 是一个由 Michał Marczyk 维护的贡献项目,他是 Clojure 社区中一位受人尊敬的贡献者。并不是说其他​​库的作者不是,只是我(作为 Clojure 新手)不认识他们。 (2认同)

Bil*_*ick 17

我知道这个问题已经过时了,但是没有人对差异列表说过什么,因为你说你真的只想要一些你可以追加和添加的东西,听起来差异列表可能对你有帮助.它们在Clojure中似乎并不受欢迎,但是它们非常容易实现,并且比手指树复杂得多,所以我刚才创建了一个微小的差异列表库(甚至测试过它).这些在O(1)时间(前置或附加)中连接.将差异列表转换回列表应该花费你O(n),如果你做了大量的连接,这是一个很好的权衡.如果你没有进行大量连接,那么只需坚持列表,对吗?:)

以下是这个小型库中的函数:

dl:差异列表实际上是一个函数,它使用参数连接自己的内容并返回结果列表.每次生成差异列表时,您都会创建一个像数据结构一样的小函数.

dlempty:由于差异列表只是将其内容连接到参数,因此空差异列表与身份函数相同.

undl:由于列表有什么不同,你可以通过用nil调用它来将差异列表转换为普通列表,所以这个函数并不是真正需要的.这只是为了方便.

dlcons:将一个项目集中到列表的前面 - 并非完全必要,但是consing是一个足够普通的操作,它只是一个单行程序(就像所有函数一样).

dlappend:收集两个不同的名单.我认为它的定义是最有趣的 - 看看吧!:)

现在,这是一个很小的库 - 5个单行函数,它们为您提供O(1)追加/前置数据结构.不错,嗯?啊,Lambda Calculus的美丽......

(defn dl
  "Return a difference list for a list"
  [l]
  (fn [x] (concat l x)))

; Return an empty difference list
(def dlempty identity)

(defn undl
  "Return a list for a difference list (just call the difference list with nil)"
  [aDl]
  (aDl nil))

(defn dlcons
  "Cons an item onto a difference list"
  [item aDl]
  (fn [x] (cons item (aDl x))))

(defn dlappend
  "Append two difference lists"
  [dl1 dl2]
  (fn [x] (dl1 (dl2 x))))
Run Code Online (Sandbox Code Playgroud)

你可以看到它的实际效果:

(undl (dlappend (dl '(1 2 3)) (dl '(4 5 6))))
Run Code Online (Sandbox Code Playgroud)

返回:

(1 2 3 4 5 6)
Run Code Online (Sandbox Code Playgroud)

这也返回相同的东西:

((dl '(1 2 3)) '(4 5 6))
Run Code Online (Sandbox Code Playgroud)

与差异列表玩得开心!

更新

以下是一些可能更难理解的定义,但我认为更好:

(defn dl [& elements] (fn [x] (concat elements x)))
(defn dl-un [l] (l nil))
(defn dl-concat [& lists] (fn [x] ((apply comp lists) x)))
Run Code Online (Sandbox Code Playgroud)

这让你说出这样的话:

(dl-un (dl-concat (dl 1) (dl 2 3) (dl) (dl 4)))
Run Code Online (Sandbox Code Playgroud)

哪会回来

(1 2 3 4)
Run Code Online (Sandbox Code Playgroud)


drc*_*ode 5

如果您不害怕准引用,这个解决方案实际上非常优雅(对于“优雅”的某些定义):

> `[~:foo ~@[:bar :baz]]

[:foo :bar :baz]
Run Code Online (Sandbox Code Playgroud)

实际上,我有时会在实际代码中使用它,因为声明性语法使其非常具有可读性(恕我直言)。