2017-09-21 25 views
7

Là một người mới đến Clojure, tôi thường gặp khó khăn trong việc thể hiện những điều đơn giản nhất. Ví dụ, để thay thế các yếu tố cuối cùng trong một véc tơ, đó sẽ làCách thay thế phần tử cuối cùng trong một vector trong Clojure

v[-1]=new_value 

trong python, tôi kết thúc với các biến thể sau đây trong Clojure:

(assoc v (dec (count v)) new_value) 

đó là khá dài và không có thần sắc để nói ít nhất, hoặc

(conj (vec (butlast v)) new_value) 

thậm chí còn tồi tệ hơn, vì nó có thời gian chạy là O(n).

Điều đó khiến tôi cảm thấy ngớ ngẩn, giống như một thượng cổ đang cố gắng sửa chữa đồng hồ Thụy Sĩ với một câu lạc bộ.

Cách Clojure phù hợp để thay thế phần tử cuối cùng trong vectơ là gì?


Để hỗ trợ O(n) -claim tôi cho butlast -version (Clojure 1.8):

(def v (vec (range 1e6))) 
#'user/v 
user=> (time (first (conj (vec (butlast v)) 55))) 
"Elapsed time: 232.686159 msecs" 
0 
(def v (vec (range 1e7))) 
#'user/v 
user=> (time (first (conj (vec (butlast v)) 55))) 
"Elapsed time: 2423.828127 msecs" 
0 

Vì vậy, về cơ bản cho 10 lần số lượng các yếu tố đó là chậm hơn 10 lần.

+1

Cách đầu tiên của bạn là cách thực hiện. Rõ ràng, bạn có thể viết một hàm "thay thế cuối cùng" để làm sạch nó. Tôi nghĩ rằng cách thể hiện quá ngắn gọn của Python đã làm hỏng mong đợi của bạn. Tôi không nghĩ rằng chỉ mục từ phía sau là cần thiết thường xuyên đủ để cảnh báo cú pháp của riêng mình trong Clojure. 1 vì tôi muốn được chứng minh là sai. – Carcigenicate

+0

Lưu ý: giải pháp # 3 không phải là O (n) nếu 'v' đã là một vectơ (tôi khuyên bạn nên luôn sử dụng một vector Clojure trong danh sách Clojure làm lựa chọn mặc định, trừ khi đo lường chứng minh khác). –

+1

Đây là câu hỏi và câu trả lời được phân tách rõ ràng, nhưng tôi nghĩ rằng rất đáng để chỉ ra rằng nhiều thuật toán được biểu diễn bằng các biểu thức chỉ mục trong Python có một tương tự Clojure thành ngữ không yêu cầu chỉ mục. – glts

Trả lời

9

Tôi muốn sử dụng

(defn set-top [coll x] 
    (conj (pop coll) x)) 

Ví dụ,

(set-top [1 2 3] :a) 
=> [1 2 :a] 

Nhưng nó cũng hoạt động trên các trước danh sách:

(set-top '(1 2 3) :a) 
=> (:a 2 3) 

Các đống chức năng Clojure - peek , popconj - hoạt động trên đầu mở tự nhiên của một bộ sưu tập tuần tự.

Nhưng không có cách nào đúng.


Các giải pháp khác nhau phản ứng với một véc tơ trống như thế nào?

  • Python của bạn v[-1]=new_value ném một ngoại lệ, cũng như (assoc v (dec (count v)) new_value) của bạn và (defn set-top [coll x] (conj (pop coll) x)) tôi.
  • (conj (vec (butlast v)) new_value) trả về [new_value]. butlast không có hiệu lực.
1

Nếu bạn khăng khăng là "tinh khiết", giải pháp thứ 2 hoặc thứ 3 của bạn sẽ hoạt động. Tôi thích được đơn giản & rõ ràng hơn bằng cách sử dụng chức năng helper from the Tupelo library:

(s/defn replace-at :- ts/List 
    "Replaces an element in a collection at the specified index." 
    [coll  :- ts/List 
    index :- s/Int 
    elem  :- s/Any] 
    ...) 

(is (= [9 1 2] (replace-at (range 3) 0 9))) 
(is (= [0 9 2] (replace-at (range 3) 1 9))) 
(is (= [0 1 9] (replace-at (range 3) 2 9))) 
As with drop-at, replace-at will throw an exception for invalid values of index. 

chức năng helper tương tự tồn tại cho

  • chèn-at
  • thả tại
  • thêm vào trước
  • thêm

Lưu ý rằng tất cả các công việc trên đều tốt cho một danh sách Clojure (háo hức hoặc lười biếng) hoặc một vector Clojure. The conj solution will fail trừ khi bạn cẩn thận để luôn ép buộc đầu vào vào vectơ trước như trong ví dụ của bạn.

+3

và mức tăng so với 'assoc' là gì? – ead

Các vấn đề liên quan