2010-06-28 26 views
31

Cách tốt nhất để có được loại dữ liệu hàng đợi đơn giản, hiệu quả trong Clojure là gì?Hàng đợi không thể thay đổi trong Clojure

Chỉ cần hai thao tác, enqueue và dequeue với ngữ nghĩa thông thường.

Tôi coi danh sách và vectơ tất nhiên, nhưng tôi hiểu rằng chúng có hiệu suất tương đối kém (tức là O (n) hoặc tệ hơn) để sửa đổi ở cuối và đầu tương ứng - vì vậy không lý tưởng cho hàng đợi!

Lý tưởng nhất là tôi muốn có cấu trúc dữ liệu liên tục thích hợp với O (log n) cho cả hoạt động enqueue và dequeue.

+1

Để lưu một người nào đó viết về cách danh sách khuyết điểm có thể được sử dụng để triển khai ngăn xếp đẩy (như tôi gần như đã làm), đừng quên câu hỏi hỏi về * hàng đợi *. :-) –

+1

Chỉ cần chú ý rằng có một lớp được gọi là PersistentQueue trong nguồn Java mới nhất có thể là câu trả lời cho câu hỏi của tôi – mikera

+6

Nó ở trong đó mãi mãi (chỉ cần kiểm tra với 1,1, nhưng tôi nghĩ nó cũ hơn hơn thế). Lưu ý rằng không có chức năng nhà máy cũng như cú pháp trình đọc cho nó được cung cấp theo mặc định; sử dụng 'clojure.lang.PersistentQueue/EMPTY' để lấy một thể hiện trống. Sau đó, 'conj',' pop' & 'peek' hoạt động giống như hàng đợi. Xem ví dụ câu trả lời của tôi cho câu hỏi này: http://stackoverflow.com/questions/2760017 cho một số mã được viết bằng cả 'c.l.PQ' và' LinkedBlockingQueue' của Java. –

Trả lời

34

Sự cố được giải quyết - giải pháp cho những người khác có thể thấy hữu ích.

Tôi đã tìm thấy Clojure có lớp clojure.lang.PersistentQueue thực hiện những gì cần thiết.

Bạn có thể tạo một thể hiện như thế này:

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

Theo như tôi thấy, bạn đang cần phải sử dụng Java interop để tạo ra các ví dụ nhưng như Michal helpfully chỉ ra bạn có thể sử dụng cái nhìn, pop và sau đó.

+6

PersistentQueue thực sự là lựa chọn tốt nhất của bạn. Để tham khảo trong tương lai, dưới đây là bảng tóm tắt các đặc tính hiệu suất/sự đảm bảo của cấu trúc dữ liệu clojure: http://www.innoq.com/blog/st/2010/04/clojure_performance_guarantees.html – dvogel

+0

Tại sao lại sử dụng 'atom'? –

+0

@ raxod502 Có gì sai khi sử dụng nguyên tử trong tình huống này không? – dgellow

6

Tôi sử dụng chức năng sau queue để tạo PersistentQueue. Tùy chọn, bạn có thể muốn có phương thức in và trình đọc dữ liệu nếu bạn đang in và đọc hàng đợi.

Các chức năng Clojure thông thường đã được triển khai cho PersistentQueue.

  • cái nhìn - nhận người đứng đầu
  • pop - trả về một PersistentQueue mới mà không có sự đầu
  • conj - thêm mục vào đuôi
  • trống rỗng? - đúng nếu rỗng
  • seq - nội dung như là một chuỗi (danh sách)

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

Clojure thực sự có thể được hưởng lợi từ một hàng đợi theo nghĩa đen. Điều này sẽ sạch hơn (và di động hơn) hơn là dựa vào Java interop.

Tuy nhiên, không khó để cuộn hàng đợi liên tục di động của riêng bạn, chỉ cần sử dụng các đồ gia dụng thông thường như danh sách.

Xem xét hàng đợi dưới dạng hai danh sách, một danh sách cung cấp phần đầu của hàng đợi và phần còn lại của đuôi. enqueue thêm vào danh sách đầu tiên, dequeue bật từ sau. Hầu hết các chức năng ISeq được thực hiện một cách trivially.

Có lẽ phần khó khăn duy nhất là điều gì xảy ra khi đuôi trống và bạn muốn dequeue. Trong trường hợp đó danh sách đầu là reverse d và trở thành đuôi mới và danh sách trống sẽ trở thành danh sách đầu mới. Tôi tin rằng, ngay cả với chi phí của reverse, rằng enqueuedequeue vẫn là O(1), mặc dù số k sẽ cao hơn vectơ vani.

Chúc mừng queue ing!

Các vấn đề liên quan