在Clojure中获取简单,高效的不可变队列数据类型的最佳方法是什么?
它只需要两个操作,用通常的语义排队和出列。
我当然考虑了列表和向量,但我知道它们在结尾和开始时的性能相对较差(即O(n)或更差) - 因此不适合队列!
理想情况下,我想要一个适当的持久数据结构,其中包含入队和出列操作的O(log n)。
答案 0 :(得分:34)
问题已解决 - 为可能觉得有帮助的其他人提供解决方案。
我发现Clojure有clojure.lang.PersistentQueue类可以完成所需的操作。
您可以创建如下实例:
(def x (atom clojure.lang.PersistentQueue/EMPTY))
据我所知,您目前需要使用Java互操作来创建实例,但正如Michal所指出的那样,您可以随后使用peek,pop和conj。
答案 1 :(得分:7)
我使用以下函数queue
来创建PersistentQueue。或者,如果您打算打印并阅读队列,则可能需要打印方法和数据读取器。
PersistentQueue已经实现了通常的Clojure函数。
(defn queue
([] clojure.lang.PersistentQueue/EMPTY)
([coll] (reduce conj clojure.lang.PersistentQueue/EMPTY coll)))
(defmethod print-method clojure.lang.PersistentQueue
[q ^java.io.Writer w]
(.write w "#queue ")
(print-method (sequence q) w))
(comment
(let [*data-readers* {'queue #'queue}]
(read-string (pr-str (queue [1 2 3])))))
答案 2 :(得分:1)
Clojure确实可以从队列文字中受益。这比依赖Java互操作更干净(也更便携)。
但是,使用像列表这样的普通家庭用品来推送自己的便携式持久性队列并不难。
将队列视为两个列表,一个列表提供队列的头部,另一个列表尾部。 enqueue
添加到第一个列表中,dequeue
来自后者。大多数ISeq
函数都可以轻松实现。
可能唯一棘手的部分是当尾巴是空的并且你想要dequeue
时会发生什么。在这种情况下,头部列表为reverse
d并成为新的尾部,空列表成为新的头部列表。我相信,即使有reverse
的开销,enqueue
和dequeue
仍为O(1)
,但k
将高于香草矢量当然。
快乐queue
ing!