Di Clojure, kapan saya harus menggunakan vektor di atas daftar, dan sebaliknya?

147

Saya membaca bahwa Vektor bukan seqs, tetapi Daftar. Saya tidak yakin apa alasannya menggunakan satu di atas yang lain. Tampaknya vektor yang paling banyak digunakan, tetapi apakah ada alasan untuk itu?

Rayne
sumber
1
Terkait stackoverflow.com/questions/6928327/…
Duncan McGregor

Jawaban:

112

Sekali lagi, sepertinya saya sudah menjawab pertanyaan saya sendiri dengan menjadi tidak sabar dan menanyakannya di #clojure di Freenode. Untung menjawab pertanyaan Anda sendiri dianjurkan di Stackoverflow.com: D

Saya berdiskusi singkat dengan Rich Hickey, dan inilah intinya.

[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
Rayne
sumber
Saat Anda menggunakan freenode, datanglah ke sisi gelap dan bergabunglah dengan #stackoverflow! :-P
Chris Jester-Young
Saya benar-benar terbiasa menganggur di sana. Saya beralih klien IRC dan tidak pernah berpikir untuk menambahkan #stackoverflow ke daftar autojoin saya.
Rayne
Saya seorang pemula Lisp, tetapi saya bertanya-tanya apakah vektor, peta, dan set mematahkan gagasan bahwa semua kode dapat dipertukarkan dengan data? Atau apakah ini hanya salah satu dari hal-hal itu yang membuat Clojure seorang yang praktis? (ATAU, bisakah Anda mengevaluasi vektor?)
Rob Grant
23
Cuplikan obrolan ini sama sekali tidak membantu. "Menghasilkan kode" "menghasilkan back-to-front" -> artinya tepat ?? Saya benar-benar mengalami kesulitan dengan pertanyaan ini karena dalam kemalasan buku saya + gaya deklaratif = kinerja yang jauh lebih baik, namun vektor disarankan di mana-mana di Clojure yang membuat saya benar-benar bingung.
Jimmy Hoffa
22
@JimmyHoffa Cara saya memahaminya: "Menghasilkan Kode" = "Di dalam Makro" (karena sebagian besar kode adalah panggilan fungsi, dengan demikian daftar); "menghasilkan kembali ke depan" = "membangun urutan dengan menambahkan sebelumnya".
omiel
87

Jika Anda sudah sering melakukan pemrograman Java, dan terbiasa dengan kerangka koleksi Java, pikirkan daftar like LinkedList, dan vektor like ArrayList. Jadi Anda bisa memilih wadah dengan cara yang sama.

Untuk klarifikasi lebih lanjut: jika Anda berniat untuk menambahkan item satu per satu ke bagian depan atau belakang urutan, daftar yang ditautkan jauh lebih baik daripada vektor, karena item tidak perlu dikocok setiap kali. Namun, jika Anda ingin mendapatkan elemen tertentu (tidak dekat bagian depan atau belakang daftar) sering (yaitu, akses acak), Anda akan ingin menggunakan vektor.

Omong-omong, vektor dapat dengan mudah diubah menjadi seqs.

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)
Chris Jester-Young
sumber
Vektor bukan seqs, tetapi mereka berurutan. (sumber: Kaya sendiri di #clojure di freenode.) Juga, saya sama sekali tidak tahu Jawa, tetapi Rich hanya menjawab pertanyaan saya.
Rayne
1
Saya akan mengedit posting saya untuk mengatakan, vektor dapat dibuat menjadi seqs, melalui fungsi seq. :-)
Chris Jester-Young
2
Pilih jawaban Anda karena memang menjawab pertanyaan, dan saya benar-benar tidak suka memilih jawaban saya sendiri sebagai benar. Sepertinya tidak benar. Terima kasih. :)
Rayne
Deque lebih baik daripada daftar yang ditautkan dalam hal menambahkan pertama dan terakhir. LLs cukup mengerikan: P
kotak
1
@boxed Anda tidak dapat mengimplementasikan deque di atas vektor atau ArrayListtanpa, secara efektif, mengimplementasikan kembali ArrayDequediri Anda.
Chris Jester-Young
43

Vektor memiliki O (1) waktu akses acak, tetapi mereka harus preallocated. Daftar dapat diperpanjang secara dinamis, tetapi mengakses elemen acak adalah O (n).

Svante
sumber
3
Secara teknis, daftar tertaut memiliki O (1) kali akses ... jika Anda mengakses elemen depan atau belakang saja. :-P Namun, vektor memiliki O (1) akses acak. :-)
Chris Jester-Young
4
("Daftar yang ditautkan" sebagaimana dijelaskan di atas mengacu pada daftar yang ditautkan ganda. Daftar yang ditautkan sendiri memiliki O (1) akses ke elemen depan saja. :-P)
Chris Jester-Young
1
Sebagai seseorang yang menyelam ke Clojure, ini adalah jawaban yang CARA lebih baik daripada dua lainnya dengan suara lebih banyak. Dua lainnya tidak memberitahuku apa-apa.
keithjgrant
@ ChrisJester-Young Daftar single-linked dapat mendukung O (1) akses ke belakang jika menyimpan referensi ke elemen kembali, seperti itu .
Gill Bates
30

Kapan harus menggunakan vektor:

  • Kinerja akses yang diindeks - Anda mendapatkan biaya ~ O (1) untuk akses yang diindeks vs. O (n) untuk daftar
  • Menambahkan - dengan kon adalah ~ O (1)
  • Notasi yang mudah - Saya merasa lebih mudah mengetik dan membaca [1 2 3] daripada '(1 2 3) untuk daftar literal dalam keadaan di mana salah satu akan bekerja.

Kapan harus menggunakan daftar:

  • Ketika Anda ingin mengaksesnya sebagai urutan (karena daftar langsung mendukung seq tanpa harus mengalokasikan objek baru)
  • Prepending - menambahkan ke awal daftar dengan kontra atau lebih baik kon adalah O (1)
mikera
sumber
3
Bahkan ketika menambahkan / menghapus di kedua ujung daftar adalah pilihan yang cukup mengerikan Deque jauh lebih baik (di CPU dan terutama memori). Coba github.com/pjstadig/deque-clojure
kemas
2
Re: the ~O(1), bagi mereka yang memerlukan penjelasan biaya ini - stackoverflow.com/questions/200384/constant-amortized-time
Merlyn Morgan-Graham
13

hanya catatan singkat:

"Saya membaca bahwa Vektor bukan seqs, tetapi Daftar adalah." 

urutan lebih umum daripada daftar atau vektor (atau peta atau set).
Sangat disayangkan bahwa REPL mencetak daftar dan urutan yang sama karena itu benar-benar membuatnya terlihat seperti daftar adalah urutan meskipun mereka berbeda. fungsi (seq) akan membuat urutan dari banyak hal yang berbeda termasuk daftar, dan Anda kemudian dapat memberi makan seq ke salah satu dari banyak fungsi yang melakukan hal-hal bagus dengan 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 memiliki pintasan yang mengembalikan argumennya jika sudah 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);
}

daftar adalah urutan, meskipun hal-hal lain juga, dan tidak semua urutan adalah daftar.

Arthur Ulfeldt
sumber
Saya tidak bermaksud mengambil titik kecil, ini hanya kesempatan untuk menunjukkan sesuatu yang berguna. banyak yang sudah tahu ini :)
Arthur Ulfeldt
2
Bukankah maksud Anda, classbukan class??
qerub
Tidak yakin apakah contoh Anda telah berubah mengikuti pembaruan clojure (saya pikir saya di 1,5), tetapi kedua contoh Anda kembali clojure.lang.PersistentListuntuk saya. Saya berasumsi Anda bermaksud untuk classtidak menulis class?.
Adrian Mouat
Aku memang melakukannya! Saya akan memperbaikinya
Arthur Ulfeldt
Masih sedikit bingung; karena classmengembalikan PersistentList yang sama untuk kedua ekspresi yang Anda sebutkan ini, ini menyiratkan bahwa urutan dan daftar memang hal yang sama persis?
johnbakers