Trong Clojure, khi nào tôi nên sử dụng một vectơ trên một danh sách và cách khác?


Câu trả lời:


112

Một lần nữa, dường như tôi đã trả lời câu hỏi của chính mình bằng cách thiếu kiên nhẫn và hỏi nó trong #clojure trên Freenode. Điều tốt là trả lời câu hỏi của riêng bạn được khuyến khích trên Stackoverflow.com: D

Tôi đã có một cuộc thảo luận nhanh với Rich Hickey, và đây là ý chính của nó.

[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

Trong khi bạn đang ở trên freenode, hãy đến với mặt tối và tham gia #stackoverflow! :-P
Chris Jester-Young

Tôi thực sự đã sử dụng để nhàn rỗi ở đó. Tôi đã chuyển đổi ứng dụng khách IRC và không bao giờ nghĩ sẽ thêm #stackoverflow vào danh sách tự động tham gia của mình.
Rayne

Tôi là người mới Lisp, nhưng tôi tự hỏi liệu các vectơ, bản đồ và bộ có bị phá vỡ theo một cách nào đó ý tưởng rằng tất cả các mã có thể hoán đổi với dữ liệu không? Hay đây chỉ là một trong những điều khiến Clojure trở thành một Lisp thực tế? (HOẶC, bạn có thể đánh giá một vectơ không?)
Rob Grant

23
Đây là đoạn trò chuyện hoàn toàn không có ích. "Tạo mã" "tạo back-to-front" -> có nghĩa là chính xác ?? Tôi thực sự gặp khó khăn với câu hỏi này bởi vì trong cuốn sách của tôi sự lười biếng + phong cách khai báo = hiệu suất tốt hơn nhiều, và các vectơ được đề xuất ở khắp mọi nơi trong Clojure khiến tôi hoàn toàn bối rối.
Jimmy Hoffa

22
@JimmyHoffa Cách tôi hiểu: "Tạo mã" = "Bên trong một Macro" (vì hầu hết các mã là các lệnh gọi hàm, do đó liệt kê); "tạo trở lại phía trước" = "xây dựng một chuỗi bằng cách trả trước".
omiel

87

Nếu bạn đã thực hiện lập trình Java rất nhiều và quen thuộc với khung bộ sưu tập Java, hãy nghĩ đến các danh sách như LinkedListvà các vectơ như thế nào ArrayList. Vì vậy, bạn có thể khá nhiều lựa chọn container theo cùng một cách.

Để làm rõ hơn: nếu bạn có ý định thêm các mục riêng lẻ vào phía trước hoặc phía sau của chuỗi nhiều, một danh sách được liên kết sẽ tốt hơn nhiều so với một vectơ, bởi vì các mục không cần phải xáo trộn mỗi lần. Tuy nhiên, nếu bạn muốn nhận được các phần tử cụ thể (không ở gần mặt trước hoặc mặt sau của danh sách) thường xuyên (nghĩa là truy cập ngẫu nhiên), bạn sẽ muốn sử dụng vectơ.

Nhân tiện, vectơ có thể dễ dàng biến thành 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)

Các vectơ không seqs, nhưng chúng là tuần tự. (nguồn: Rich mình trên #clojure trên freenode.) Ngoài ra, tôi hoàn toàn không biết Java, nhưng Rich chỉ trả lời câu hỏi của tôi.
Rayne

1
Tôi sẽ chỉnh sửa bài viết của mình để nói, vectơ có thể được tạo thành seqs, thông qua chức năng seq. :-)
Chris Jester-Young

2
Chọn câu trả lời của bạn vì nó thực sự đã trả lời câu hỏi và tôi thực sự không thích chọn câu trả lời của riêng mình là chính xác. Có vẻ không đúng. Cảm ơn. :)
Rayne

Một deque là tốt hơn so với một danh sách liên kết trong trường hợp thêm đầu tiên và cuối cùng. LL khá khủng khiếp: P
đóng hộp

1
@boxed Bạn không thể thực hiện một deque trên đầu của một vectơ hoặc ArrayListkhông, một cách hiệu quả, ArrayDequetự thực hiện lại chính mình.
Chris Jester-Young

43

Các vectơ có O (1) thời gian truy cập ngẫu nhiên, nhưng chúng phải được đặt trước. Danh sách có thể được mở rộng động, nhưng truy cập một yếu tố ngẫu nhiên là O (n).


3
Về mặt kỹ thuật, danh sách được liên kết có thời gian truy cập O (1) ... nếu bạn chỉ truy cập phần tử trước hoặc sau. :-P Tuy nhiên, vectơ có quyền truy cập ngẫu nhiên O (1). :-)
Chris Jester-Young

4
("Danh sách được liên kết" như được mô tả ở trên đề cập đến danh sách được liên kết đôi. Danh sách được liên kết đơn chỉ có quyền truy cập O (1) vào phần tử phía trước. :-P)
Chris Jester-Young

1
Là một người chỉ cần lao vào Clojure, đây là một câu trả lời tốt hơn so với hai người khác có nhiều phiếu bầu hơn. Hai người kia nói với tôi không có gì sử dụng.
keithjgrant

@ ChrisJester-Young Danh sách liên kết đơn có thể hỗ trợ truy cập O (1) ở phía sau nếu nó lưu trữ một tham chiếu đến phần tử phía sau, như thế .
Gill Bates

30

Khi nào nên sử dụng vectơ:

  • Hiệu suất truy cập được lập chỉ mục - Bạn nhận chi phí ~ O (1) cho truy cập được lập chỉ mục so với O (n) cho danh sách
  • Nối thêm - với liên hợp là ~ O (1)
  • Ký hiệu thuận tiện - Tôi thấy cả việc nhập và đọc [1 2 3] dễ dàng hơn so với '(1 2 3) cho một danh sách theo nghĩa đen trong trường hợp một trong hai sẽ hoạt động.

Khi nào nên sử dụng danh sách:

  • Khi bạn muốn truy cập nó dưới dạng một chuỗi (vì danh sách hỗ trợ trực tiếp seq mà không phải phân bổ các đối tượng mới)
  • Chuẩn bị - thêm vào đầu danh sách với khuyết điểm hoặc tốt nhất là liên hợp là O (1)

3
Ngay cả khi thêm / xóa ở cả hai đầu, một danh sách là một lựa chọn khá khủng khiếp. Một deque là tốt hơn nhiều (trong CPU và đặc biệt là bộ nhớ). Hãy thử github.com/pjstadig/deque-clojure
đóng hộp

2
Re: the ~O(1), đối với những người mà giải thích chi phí này có thể hữu ích - stackoverflow.com/questions/200384/constant-amortized-time
Merlyn Morgan-Graham

13

chỉ là một ghi chú bên nhanh:

"Tôi đọc rằng vectơ không phải là seqs, nhưng Danh sách thì có." 

trình tự chung chung hơn danh sách hoặc vectơ (hoặc bản đồ hoặc bộ).
Thật không may là REPL in danh sách và trình tự giống nhau bởi vì nó thực sự làm cho nó trông giống như danh sách là trình tự mặc dù chúng khác nhau. Hàm (seq) sẽ tạo một chuỗi từ rất nhiều thứ khác nhau, bao gồm cả danh sách, và sau đó bạn có thể cung cấp seq đó cho bất kỳ rất nhiều chức năng thực hiện những điều tiện lợi bằng 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 có một phím tắt trả về đối số của nó nếu nó đã là một 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);
}

danh sách là các chuỗi, mặc dù những thứ khác cũng vậy, và không phải tất cả các chuỗi đều là danh sách.


Tôi không có ý chọn một điểm nhỏ, đó chỉ là cơ hội để chỉ ra điều gì đó hữu ích. nhiều người sẽ biết điều này :)
Arthur Ulfeldt

2
Ý bạn là classthay vì class??
qerub

Không chắc chắn nếu ví dụ của bạn đã thay đổi sau các cập nhật clojure (tôi nghĩ tôi đang trên 1.5), nhưng cả hai ví dụ của bạn đều trả lại clojure.lang.PersistentListcho tôi. Tôi cho rằng bạn có nghĩa là classkhông viết class?.
Adrian Mouat

Tôi đã thực sự! Tôi sẽ sửa nó
Arthur Ulfeldt

Vẫn còn một chút bối rối; vì classtrả về cùng một PersistentList cho cả hai biểu thức bạn đã đề cập, điều này ngụ ý rằng các chuỗi và danh sách có thực sự giống nhau không?
johnbakers
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.