In Clojure, quando dovrei usare un vettore su un elenco e viceversa?


Risposte:


112

Ancora una volta, sembra che abbia risposto alla mia domanda diventando impaziente e facendolo in #clojure su Freenode. La cosa buona è rispondere alle tue domande su Stackoverflow.com: D

Ho avuto una breve discussione con Rich Hickey, ed ecco il senso.

[12:21] <Raynes>    Vectors aren't seqs, right?
[12:21] <rhickey>   Raynes: no, but they are sequential
[12:21] <rhickey>   ,(sequential? [1 2 3])
[12:21] <clojurebot>    true
[12:22] <Raynes>    When would you want to use a list over a vector?
[12:22] <rhickey>   when generating code, when generating back-to-front
[12:23] <rhickey>   not too often in Clojure

Mentre sei su freenode, vieni sul lato oscuro e unisciti a #stackoverflow! :-P
Chris Jester-Young,

In realtà ero solito inattivo lì. Ho cambiato client IRC e non ho mai pensato di aggiungere #stackoverflow al mio elenco di autojoin.
Rayne,

Sono un principiante Lisp, ma mi chiedevo se vettori, mappe e set rompessero in qualche modo l'idea che tutto il codice sia intercambiabile con i dati? O è solo una di quelle cose che rendono Clojure un Lisp pratico? (O, puoi valutare un vettore?)
Rob Grant,

23
Questo è uno snippet di chat completamente inutile. "Generare codice" "generare back-to-front" -> significa precisamente ?? Ho davvero difficoltà con questa domanda perché nel mio libro pigrizia + stile dichiarativo = prestazioni molto migliori, eppure i vettori sono suggeriti ovunque in Clojure, il che mi lascia totalmente confuso.
Jimmy Hoffa,

22
@JimmyHoffa Il modo in cui lo capisco: "Generating Code" = "Inside a Macro" (perché la maggior parte del codice sono chiamate di funzione, quindi elenchi); "generate back to front" = "costruendo una sequenza anteponendo".
omiel

87

Se hai programmato molto Java e hai familiarità con il framework di raccolta Java, pensa a liste come LinkedListe vettori simili ArrayList. Quindi puoi praticamente scegliere i contenitori allo stesso modo.

Per ulteriori chiarimenti: se si intende aggiungere elementi singolarmente all'inizio o alla fine della sequenza, un elenco collegato è molto meglio di un vettore, poiché gli elementi non devono essere mescolati ogni volta. Tuttavia, se si desidera accedere frequentemente a elementi specifici (non vicino alla parte anteriore o posteriore dell'elenco) (ad esempio, accesso casuale), è necessario utilizzare il vettore.

A proposito, i vettori possono essere facilmente trasformati in seq.

user=> (def v (vector 1 2 3))
#'user/v
user=> v
[1 2 3]
user=> (seq v)
(1 2 3)
user=> (rseq v)
(3 2 1)

I vettori non sono seq, ma sono sequenziali. (fonte: Rich stesso su #clojure su freenode.) Inoltre, non conosco affatto Java, ma Rich ha appena risposto alla mia domanda.
Rayne,

1
Modificherò il mio post per dire che i vettori possono essere trasformati in seqs, tramite la funzione seq. :-)
Chris Jester-Young,

2
Scegli la tua risposta perché in realtà ha risposto alla domanda e non mi piace davvero scegliere le mie risposte come corrette. Non sembra giusto. Grazie. :)
Rayne,

Una deque è meglio di una lista collegata in caso di aggiunta prima e ultima. LLS sono abbastanza terribile: P
boxed

1
@boxed Non puoi implementare un deque sopra un vettore o ArrayListsenza, effettivamente, reimplementarti ArrayDeque.
Chris Jester-Young

43

I vettori hanno tempi di accesso casuali O (1), ma devono essere preallocati. Gli elenchi possono essere estesi dinamicamente, ma l'accesso a un elemento casuale è O (n).


3
Tecnicamente, gli elenchi collegati hanno tempi di accesso O (1) ... se si accede solo all'elemento anteriore o posteriore. :-P Tuttavia, i vettori hanno O (1) accesso casuale. :-)
Chris Jester-Young,

4
("Elenco collegato" come descritto sopra si riferisce a elenchi doppiamente collegati. Gli elenchi collegati singolarmente hanno accesso O (1) solo all'elemento frontale. :-P)
Chris Jester-Young,

1
Dato che qualcuno si sta semplicemente tuffando in Clojure, questa è una risposta MOLTO migliore delle altre due con più voti. Gli altri due non mi dicono nulla di utile.
keithjgrant,

@ ChrisJester-Young L'elenco a link singolo può supportare l'accesso O (1) alla parte posteriore se memorizza un riferimento all'elemento back, come quello .
Gill Bates,

30

Quando usare un vettore:

  • Prestazioni di accesso indicizzato: ottieni ~ O (1) costo per accesso indicizzato rispetto a O (n) per gli elenchi
  • In aggiunta - con conj è ~ O (1)
  • Notazione conveniente: trovo sia più facile digitare e leggere [1 2 3] di '(1 2 3) per un elenco letterale in circostanze in cui entrambi funzionerebbero.

Quando utilizzare un elenco:

  • Quando si desidera accedervi come sequenza (poiché gli elenchi supportano direttamente seq senza dover allocare nuovi oggetti)
  • Prepending - l'aggiunta all'inizio di un elenco con contro o preferibilmente conj è O (1)

3
Anche quando si aggiunge / rimuove su entrambi i lati un elenco è una scelta piuttosto terribile. Un deque è molto meglio (nella CPU e soprattutto nella memoria). Prova github.com/pjstadig/deque-clojure
scatola il

2
Ri: the ~O(1), per coloro ai quali questa spiegazione dei costi potrebbe essere utile - stackoverflow.com/questions/200384/constant-amortized-time
Merlyn Morgan-Graham

13

solo una breve nota a margine:

"Ho letto che i vettori non sono seq, ma gli elenchi lo sono." 

le sequenze sono più generiche di elenchi o vettori (o mappe o set).
È un peccato che REPL stampi gli elenchi e le sequenze allo stesso modo perché fa sembrare che gli elenchi siano sequenze anche se sono diversi. la funzione (seq) farà una sequenza da molte cose diverse tra cui gli elenchi, e puoi quindi alimentare quel seq a una qualsiasi delle tante funzioni che fanno cose belle con seqs.

user> (class (list 1 2 3))
clojure.lang.PersistentList

user> (class (seq (list 1 2 3)))
clojure.lang.PersistentList

user> (class (seq [1 2 3]))
clojure.lang.PersistentVector$ChunkedSeq

Sec ha un collegamento che restituisce il suo argomento se è già un seq:

user> (let [alist (list 1 2 3)] (identical? alist (seq alist)))
true
user> (identical? (list 1 2 3) (seq (list 1 2 3)))
false

static public ISeq seq(Object coll){
        if(coll instanceof ASeq)
                return (ASeq) coll;
        else if(coll instanceof LazySeq)
                return ((LazySeq) coll).seq();
        else
                return seqFrom(coll);
}

gli elenchi sono sequenze, anche se lo sono anche altre cose, e non tutte le sequenze sono elenchi.


Non intendo scegliere un piccolo punto, è solo un'opportunità per sottolineare qualcosa di utile. molti lo sapranno già :)
Arthur Ulfeldt,

2
Non intendi classinvece class??
qerub,

Non sono sicuro che il tuo esempio sia cambiato a seguito degli aggiornamenti di clojure (penso di essere in 1.5), ma entrambi i tuoi esempi tornano clojure.lang.PersistentListper me. Sto assumendo si intende scrivere classnon class?.
Adrian Mouat,

L'ho fatto davvero! Lo aggiusterò
Arthur Ulfeldt il

Ancora un po 'confuso; dato che classrestituisce la stessa PersistentList per entrambe queste espressioni che hai menzionato, questo implica che sequenze ed elenchi sono davvero la stessa cosa?
johnbakers,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.