2010-06-23 35 views
12

tôi đã viết chức năng này mà thực hiện điều này (dễ dàng hơn để hiển thị hơn giải thích):Cách Clojure thành ngữ nhất để viết cái này là gì?

(split 2 (list 1 2 3 4 5 6))

=> ((1 2) (2 3) (3 4) (4 5) (5 6))

(defn split [n xs] 
    (if (> (count xs) (dec n)) 
     (cons (take n xs) (split n (rest xs))) 
     '())) 

Tôi hiểu rằng trong Clojure danh sách không chỉ là cấu trúc lớp dữ liệu đầu tiên. Nó sẽ có ý nghĩa để viết cấu trúc dữ liệu này-thuyết bất khả tri? Và bất kể, việc triển khai của tôi có hiệu quả nhất hay không, làm thế nào tôi có thể làm cho nó hiệu quả hơn và/hoặc thành ngữ?

Cảm ơn!

Trả lời

21

Bạn có thể sử dụng được xây dựng trong chức năng phân vùng,

(partition 2 1 (list 1 2 3 4 5 6)) 
=> ((1 2) (2 3) (3 4) (4 5) (5 6)) 

công trình đối với bất kỳ trình tự.

 

clojure.core/partition 
([n coll] [n step coll] [n step pad coll]) 
    Returns a lazy sequence of lists of n items each, at offsets step 
    apart. If step is not supplied, defaults to n, i.e. the partitions 
    do not overlap. If a pad collection is supplied, use its elements as 
    necessary to complete last partition upto n items. In case there are 
    not enough padding elements, return a partition with less than n items. 
 
2

Tôi đã sử dụng Clojure khoảng một tháng, vì vậy tôi có lẽ không đủ điều kiện để bổ nhiệm cách thành ngữ nhất;)

Nhưng thực hiện của bạn là ngắn và đến điểm (bỏ qua nó cũng sao chép hàm tích hợp partition như đã đề cập).

Việc thực hiện đã khá cấu trúc dữ liệu thuyết bất khả tri - vì nó sử dụng sequence hoạt động, nó hoạt động với tất cả các cấu trúc dữ liệu tiêu chuẩn:

(split 2 [1 2 3 4 5 6]) 
=> ((1 2) (2 3) (3 4) (4 5) (5 6)) 

(split 2 #{1 2 3 4 5 6}) 
=> ((1 2) (2 3) (3 4) (4 5) (5 6)) 

(split 2 {1 :a 2 :b 3 :c 4 :d}) 
=> (([1 :a] [2 :b]) ([2 :b] [3 :c]) ([3 :c] [4 :d])) 

(split 2 "abcd") 
=> ((\a \b) (\b \c) (\c \d)) 

Hạn chế chính của việc sử dụng đệ quy đơn giản là bạn bị giới hạn bởi kích thước của ngăn xếp:

(split 2 (range 10000)) 
=> java.lang.StackOverflowError 

Vì vậy, nếu bạn đang mong đợi đầu vào kích thước nhiều trên 1k, nó tốt hơn để sử dụng vòng lặp/tái diễn, mà không sử dụng ngăn xếp:

(defn split-loop [n coll] 
    (loop [elms coll res [] ] 
    (if (< (count elms) n) 
     res 
     (recur (next elms) (conj res (take n elms)))))) 
5

Không cần phải viết triển khai của riêng bạn. Clojure cung cấp phân vùng, là lười biếng. Cũng không cần phải sử dụng danh sách, nếu bạn sử dụng chỉ số literals:

(partition 2 '(1 2 3 4 5 6)) 
5

Bạn có thể tạo ra một chuỗi lười biếng ra khỏi phiên bản của bạn:

(defn split [n xs] 
    (lazy-seq 
     (let [m (take n xs)] 
      (if (= n (count m)) 
      (cons m (split n (rest xs))))))) 

(lý do cho điều kiện khác với của bạn '(if (> (count xs) (dec n))' là bởi vì hiệu quả hơn để đếm M yếu tố ra khỏi XS thay vì đếm toàn bộ XS bộ sưu tập mỗi lần (đó là loại chống lại sự lười biếng, bởi vì chúng tôi không muốn đi bộ toàn bộ bộ sưu tập)

Hãy tưởng tượng những gì nó sẽ giống như đếm các yếu tố trong phạm vi quái dị mỗi lần lặp lại :)

(take 10 (split 2 (range 100000000000))) 

    => ((0 1) (1 2) (2 3)...) 
+0

Gọn gàng, nhờ đầu. Làm cho thay đổi duy nhất - đếm 'take n' chứ không phải là trình tự - để vòng lặp/recur phiên bản giảm thời gian từ 3000ms đến 20ms cho một phạm vi 10k ...Tôi sẽ phải nhớ rằng phần tiếp theo/phần còn lại trả về một chuỗi, trong đó đếm là O (n). –

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