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.