2012-11-12 18 views
7

Qual è la migliore struttura dati per uno stack di lunghezza fissa (inizialmente l'ho chiamato una coda, ma quello che voglio è uno stack) dove gli elementi vengono aggiunti in primo piano e ogni volta che viene aggiunto un elemento in primo piano un oggetto viene rimosso dalla fine? Si accede anche a varie lunghezze di subvettori dalla parte anteriore. Stavo usando i vettori, ora penso a clojure.lang.PersistentQueue e finger trees.Struttura stack in lunghezza fissa in Clojure

modificare, per chiarire, qualcosa come:

> (def q (queue [1 2 3 4])) 
[1 2 3 4] 
> (push q 9 8 7) 
[7 8 9 1] 
> (peek (push q 9 8 7)) 
7 

EDIT2: grazie per tutte le vostre risposte finora, questo è trasformato in un esercizio di tornare alle origini e la lettura di Gioia del Clojure, imparando per esempio che subvec di subvec mantiene un riferimento al vettore del primo subvec, mentre qualcosa del genere (vec (cons x (subvec ... sarebbe se usato ripetutamente accresca i riferimenti a tutti i subvec intermedi.) Alla luce di questo, che ne dici di questa implementazione di push per un vettore -based queue?:

(defn push [v i n] (if (>= (count v) n) (conj (subvec v 1 n) i) (conj v i))) 

allora il vettore risultante è stato possibile accedere tramite rseq che credo sia veloce con vettori (conseguente al suo impiego dell'indice-offset?)

risposta

6

Dai un'occhiata alla buffer circolare di Amalloy a https://github.com/amalloy/ring-buffer

+4

dangit! Rubando la mia reputazione SO collegandomi alla mia biblioteca? Io, ragazzo, naturalmente. In realtà sono curioso di come l'hai trovato, dal momento che non l'ho affatto pubblicizzato e una ricerca su google per "buffer anello clojure" non mostra nulla di terribilmente facile. – amalloy

+0

L'ho trovato in Google ad un certo punto e ora è nei miei preferiti;). Grazie! – DanLebrero

+1

scusa per chiarire, ring-buffer sarebbe perfetto se i suoi articoli fossero aggiunti e sbirciati nella parte anteriore ed espulsi dalla fine. È lo stesso problema che ho avuto con PersistentQueue: conj aggiunge alla fine ma sbircia in prima fila, ma mi interessa solo gli elementi più recenti (lifo) con gli elementi più vecchi che vengono rimossi per primi – Hendekagon

1

IMO è possibile utilizzare solo un elenco:

(defn create-queue [len] 
    (atom (repeat len nil))) 

(defn push [queue new-items] 
    (let [len (count @queue) 
     len2 (count new-items)] 
    (if (>= len2 len) 
     (let [res (concat (take-last (- len2 len) new-items) 
         @queue)] 
     (reset! queue (take len new-items)) 
     res) 
     (let [res (take-last len2 @queue)] 
     (reset! queue (concat new-items 
           (drop-last len2 @queue))) 
     res)))) 

prova:

(def q (create-queue 4)) 

(take 4 @q) 
-> (nil nil nil nil) 
(push q [1 2 3]) 
-> (nil nil nil) 
(take 4 @q) 
-> (1 2 3 nil) 
(push q [4 5]) 
-> (3 nil) 
(take 4 @q) 
-> (4 5 1 2) 
(push q [6 7 8 9]) 
-> (4 5 1 2) 
(take 4 @q) 
-> (6 7 8 9) 
(push q [10 11 12 13 15 16]) 
-> (15 16 6 7 8 9) 
(take 4 @q) 
-> (10 11 12 13) 
+0

ok, ma stavo già usando i vettori. Non vedo la necessità di un atomo qui – Hendekagon

+0

I ho perso l'ultima frase della tua domanda sui vettori :) No, abbiamo bisogno di stm qui.Inoltre, per la soluzione thread-safe abbiamo bisogno di ref invece di atom e fare tutto il lavoro in dosync – mobyte

+0

hmm, preferirei una struttura persistente – Hendekagon

Problemi correlati