我知道这个问题很老了,但没有人提到过差异列表。既然你只是想要可以前后追加的东西,那么差异列表听起来可能会有所帮助。虽然它们在Clojure中似乎并不流行,但它们非常容易实现,比Finger Trees复杂得多,所以我刚刚制作了一个小型的差异列表库(甚至已经测试了)。 这些列表在O(1)时间内(前缀或后缀)连接起来。将差异列表转换回列表应该花费O(n),如果您正在进行大量连接,则这是一个不错的权衡。如果您不需要进行大量连接,那么就使用列表吧?
以下是此小型库中的函数:
dl:差异列表实际上是一个函数,它将自己的内容与参数连接起来并返回结果列表。每次生成差异列表时,都会创建一个像数据结构一样的小函数。
dlempty:由于差异列表只是将其内容连接到参数中,因此空差异列表与标识函数相同。
undl:由于差异列表的功能,您可以通过使用nil调用它来将差异列表转换为正常列表,因此这个函数并不是真正需要的;它只是为了方便。
dlcons:将一个项目添加到列表的前面 - 不是完全必要的,但是连接是足够常见的操作,并且它只需要一行代码(就像这里的所有函数一样)。
dlappend:连接两个差异列表。我认为它的定义是最有趣的-来看看吧!:)
现在,这就是那个小型库--5个一行的函数,为您提供O(1)的追加/前缀数据结构。还不错吧?啊,λ演算的美妙之处...
(defn dl
"Return a difference list for a list"
[l]
(fn [x] (concat l x)))
(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))))
你可以通过以下方式查看它的作用:
(undl (dlappend (dl '(1 2 3)) (dl '(4 5 6))))
它返回:
(1 2 3 4 5 6)
这也会返回相同的结果:
((dl '(1 2 3)) '(4 5 6))
玩转差异列表吧!
更新
以下是一些可能更难理解但我认为更好的定义:
(defn dl [& elements] (fn [x] (concat elements x)))
(defn dl-un [l] (l nil))
(defn dl-concat [& lists] (fn [x] ((apply comp lists) x)))
这使您能够像这样说:
(dl-un (dl-concat (dl 1) (dl 2 3) (dl) (dl 4)))
返回的是哪一个
(1 2 3 4)
(apply vector :foo [:bar :baz])
(只需去掉cons
)。但我同意,除了(vector ...)
解决方案之外,基本上只有concat
。如果有一种漂亮的语法来展开参数而不是使用apply
(就像~@
但不仅限于宏),那该多好啊... 叹气 - chbrown