2011-03-08 29 views
44

tôi có một danh sách với danh sách nhúng của vectơ, trông giống như:Clojure: Bán cầu dẹt một chuỗi lồng nhau

(([1 2]) ([3 4] [5 6]) ([7 8]))

Mà tôi biết không phải là lý tưởng để làm việc với. Tôi muốn làm phẳng nó đến ([1 2] [3 4] [5 6] [7 8]).

flatten không hoạt động: nó mang lại cho tôi (1 2 3 4 5 6 7 8).

Làm cách nào để thực hiện việc này? Tôi hình tôi cần tạo danh sách mới dựa trên nội dung của từng mục danh sách, không phải mục và đó là phần tôi không thể tìm hiểu cách thực hiện từ tài liệu.

Trả lời

59

Nếu bạn chỉ muốn flatten nó trở thành một cấp độ bạn có thể sử dụng concat

(apply concat '(([1 2]) ([3 4] [5 6]) ([7 8]))) 
=> ([1 2] [3 4] [5 6] [7 8]) 
26

Để bật một danh sách-of-danh sách thành một danh sách duy nhất có chứa các yếu tố của mỗi tiểu-list, bạn muốn apply concat như nickik gợi ý.

Tuy nhiên, thường có giải pháp tốt hơn: không tạo danh sách danh sách để bắt đầu! Ví dụ, chúng ta hãy tưởng tượng bạn có một chức năng gọi là get-names-for mà phải mất một biểu tượng và trả về một danh sách tất cả những điều tuyệt vời bạn có thể gọi đó là biểu tượng:

(get-names-for '+) => (plus add cross junction) 

Nếu bạn muốn để có được tất cả các tên đối với một số danh sách các biểu tượng , bạn có thể thử

(map get-names-for '[+ /]) 
=> ((plus add cross junction) (slash divide stroke)) 

Nhưng điều này dẫn đến sự cố bạn đang gặp phải. Bạn có thể dán chúng lại với nhau với một apply concat, nhưng tốt hơn là nên sử dụng mapcat thay vì map để bắt đầu với:

(mapcat get-names-for '[+ /]) 
=> (plus add cross junction slash divide stroke) 
+2

Đây phải là câu trả lời được chấp nhận IMO. – ehsanul

+1

yea 1 cho mapcat - nó dẫn đến một giải pháp MUCH thanh lịch hơn tôi đã có thể khác. rất tốt để biết. cảm ơn! – jm0

8

Mã cho flatten là khá ngắn:

(defn flatten 
    [x] 
    (filter (complement sequential?) 
    (rest (tree-seq sequential? seq x)))) 

Nó sử dụng tree-seq để đi bộ thông qua cấu trúc dữ liệu và trả về một chuỗi các nguyên tử. Vì chúng tôi muốn tất cả các trình tự cấp dưới, chúng tôi có thể sửa đổi nó như sau:

(defn almost-flatten 
    [x] 
    (filter #(and (sequential? %) (not-any? sequential? %)) 
    (rest (tree-seq #(and (sequential? %) (some sequential? %)) seq x)))) 

vì vậy chúng tôi trả về tất cả các chuỗi không chứa chuỗi.

4

Ngoài ra bạn có thể tìm thấy hữu ích chung 1 mức này flatten chức năng tôi tìm thấy trên clojuremvc:

(defn flatten-1 
    "Flattens only the first level of a given sequence, e.g. [[1 2][3]] becomes 
    [1 2 3], but [[1 [2]] [3]] becomes [1 [2] 3]." 
    [seq] 
    (if (or (not (seqable? seq)) (nil? seq)) 
    seq ; if seq is nil or not a sequence, don't do anything 
    (loop [acc [] [elt & others] seq] 
     (if (nil? elt) acc 
     (recur 
      (if (seqable? elt) 
      (apply conj acc elt) ; if elt is a sequence, add each element of elt 
      (conj acc elt))  ; if elt is not a sequence, add elt itself 
     others))))) 

Ví dụ:

(flatten-1 (([1 2]) ([3 4] [5 6]) ([7 8]))) 
=>[[1 2] [3 4] [5 6] [7 8]] 

concat exampe chắc chắn làm công việc cho bạn, nhưng flatten-1 đây cũng là cho phép các phần tử non seq bên trong bộ sưu tập:

(flatten-1 '(1 2 ([3 4] [5 6]) ([7 8]))) 
=>[1 2 [3 4] [5 6] [7 8]] 
;whereas 
(apply concat '(1 2 ([3 4] [5 6]) ([7 8]))) 
=> java.lang.IllegalArgumentException: 
    Don't know how to create ISeq from: java.lang.Integer 
2

Dưới đây là một chức năng mà sẽ làm phẳng xuống mức thứ tự, bất kể làm tổ không đồng đều:

(fn flt [s] (mapcat #(if (every? coll? %) (flt %) (list %)) s)) 

Vì vậy, nếu chuỗi ban đầu của bạn là:

'(([1 2]) (([3 4]) ((([5 6])))) ([7 8])) 

Bạn sẽ vẫn nhận được kết quả tương tự:

([1 2] [3 4] [5 6] [7 8]) 
Các vấn đề liên quan