Algorithm Clojure中的不可变队列

Algorithm Clojure中的不可变队列,algorithm,data-structures,clojure,queue,immutability,Algorithm,Data Structures,Clojure,Queue,Immutability,在Clojure中获得简单、高效的不可变队列数据类型的最佳方法是什么 它只需要两个操作,按照通常的语义排队和退队 当然,我考虑了列表和向量,但我知道它们在末尾和开头分别进行修改时的性能相对较差(即O(n)或更差),因此对于队列来说并不理想 理想情况下,我希望为排队和退队操作提供一个适当的带有O(logn)的持久数据结构。问题已解决-为其他可能觉得有用的人提供解决方案 我发现Clojure具有Clojure.lang.PersistentQueue类,该类执行所需的操作 您可以创建这样的实例: (

在Clojure中获得简单、高效的不可变队列数据类型的最佳方法是什么

它只需要两个操作,按照通常的语义排队和退队

当然,我考虑了列表和向量,但我知道它们在末尾和开头分别进行修改时的性能相对较差(即O(n)或更差),因此对于队列来说并不理想


理想情况下,我希望为排队和退队操作提供一个适当的带有O(logn)的持久数据结构。

问题已解决-为其他可能觉得有用的人提供解决方案

我发现Clojure具有Clojure.lang.PersistentQueue类,该类执行所需的操作

您可以创建这样的实例:

(def x (atom clojure.lang.PersistentQueue/EMPTY))

据我所见,您目前需要使用Java interop来创建实例,但正如Michal所指出的,您可以随后使用peek、pop和conj。

我使用以下函数
queue
来创建一个PersistentQueue。或者,如果要打印和读取队列,您可能需要一个打印方法和一个数据读取器

PersistentQueue已经实现了常用的Clojure函数

  • 偷看,把头拿过来
  • pop——返回一个不带头的新PersistentQueue
  • conj——将项添加到尾部
  • 空的?--如果为空,则为true
  • seq——按顺序排列的内容(列表)

Clojure确实可以从队列文字中获益。这将比依赖Java互操作更干净(更可移植)

然而,仅仅使用列表之类的普通家用物品,就可以滚动您自己的便携式持久队列

将队列视为两个列表,一个提供队列的头部,另一个提供队列的尾部
enqueue
添加到第一个列表中,从后者弹出
dequeue
。大多数ISeq函数的实现非常简单

可能唯一棘手的部分是当尾部是空的,并且你想
出列时会发生什么。在这种情况下,标题列表是反向的,成为新的尾部,空列表成为新的标题列表。我相信,即使有
反向
的开销,
排队
退队
仍然是
O(1)
,尽管
k
当然会高于普通向量


快乐的排队

为了避免有人写关于如何使用cons列表来实现push/pop堆栈的文章(就像我几乎做的那样),请不要忘记关于队列的问题。:-)刚刚注意到在最新的1.2快照Clojure Java源代码中有一个名为PersistentQueue的类。。。。这可能是我自己问题的答案,它从永远都在那里(刚刚用1.1检查过,但我认为它比那还要老)。请注意,默认情况下没有提供工厂函数或读取器语法;使用
clojure.lang.PersistentQueue/EMPTY
获取空实例。然后,
conj
pop
peek
以队列的方式工作。例如,请参阅我对这个问题的回答:有关同时使用
c.l.PQ
和Java的
LinkedBlockingQueue
编写的一些代码。很酷,谢谢Michal!我想我一开始没注意到,因为API中没有简单的“队列”构造函数。也许我应该提交一个补丁:-)
PersistentQueue
确实是Clojure保守的秘密之一关于可能的队列相关API增强,请参阅Clojure Dev上的这个线程:注意,现在这可能是一个非常低优先级的问题,使用新的数字和all…PersistentQueue确实是您的最佳选择。以下是一个总结clojure数据结构的性能特征/保证的表格供将来参考:@raxod502在这种情况下使用atom有什么问题吗?@dgellow根据我的理解,在函数式编程语言中使用原子是一个不好的迹象,除非有充分的理由——一般认为一切都是不可变的。@RadonRosborough,……然而,在变量中使用原子肯定比变异变量本身更好。也就是说,如果您打算使用
def
(当然,这并不总是必要的!)为常数以外的其他对象命名,那么适当选择的引用类型通常是与其关联的正确对象。@charlesduff您当然是对的。我发表评论的唯一原因是,问题中没有任何东西表明需要STM或可变态,因此我发现答案使用原子是非常奇怪的,因为没有明显的原因。

    (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])))))