2011-11-11 18 views
10

Có cách nào 'thích hợp' để lặp qua chuỗi hai chiều trong Clojure không? Giả sử tôi đã có một danh sách liệt kê các con số, như thế nàyTự động lặp qua trình tự hai chiều (hoặc cao hơn) trong Clojure

((1 2 3) 
    (4 5 6) 
    (7 8 9)) 

và tôi muốn tạo ra một danh sách mới của danh sách với mỗi số tăng lên một. Có một cách dễ dàng để làm điều này trong Clojure mà không dựa vào bản đồ lồng nhau hoặc vòng lặp/đệ quy? Tôi đã có thể làm điều đó, nhưng các giải pháp của tôi rất xấu và tôi thấy họ khó hiểu khi tôi đọc lại chúng.

Cảm ơn

Trả lời

13

Bạn luôn có thể sử dụng tính năng hiểu danh sách. Tôi thấy mình sử dụng chúng khá thường xuyên đến từ một nền tảng bắt buộc vì vậy tôi không biết làm thế nào thành ngữ nó được. Trong trường hợp cụ thể của bạn, bạn có thể làm:

(for [my-list my-matrix] (map inc my-list)) 
+1

Tôi sẽ chấp nhận thế này, mặc dù những người khác chắc chắn câu trả lời hợp lệ. Điều này chỉ trikes tôi là ngắn nhất và dễ đọc nhất. – Joel

+4

Cần lưu ý rằng 'for' tạo ra một chuỗi lười biếng, vì vậy nó chỉ" lặp lại "khi giá trị được yêu cầu. – postfuturist

10

Đối với trường hợp hai chiều, bạn có thể làm một cái gì đó như:

(map #(map inc %) my-two-d-list) 

Đó không phải là quá xấu để đọc: áp dụng các chức năng #(map inc %) để mỗi phần tử trong danh sách.

Đối với trường hợp đặt hàng cao hơn, về cơ bản bạn đang nói về việc duyệt qua cây. Bạn sẽ muốn có một hàm trong một cây và một hàm, và áp dụng hàm đó cho mỗi nút trong cây. Bạn có thể tìm thấy các chức năng cho điều này trong clojure.walk.

5

Các câu trả lời khác của Sean và Matt đều thể hiện cách súc tích và hiệu quả để có kết quả đúng.

Tuy nhiên có một số phần mở rộng quan trọng bạn có thể làm như sau:

  • Nó sẽ được tốt đẹp để xử lý các trường hợp kích thước cao hơn
  • Nó là tốt để bọc các chức năng trong một hàm bậc cao

Ví dụ mã:

;; general higher order function 
(defn map-dimensions [n f coll] 
    (if (= n 1) 
    (map f coll) 
    (map #(map-dimensions (dec n) f %) coll))) 

;; use partial application to specialise to 2 dimensions 
(def map-2d (partial map-dimensions 2)) 

(map-2d inc 
    '((1 2 3) 
     (4 5 6) 
     (7 8 9))) 
=> ((2 3 4) (5 6 7) (8 9 10)) 
+0

Nó vẫn có giá trị để xem làm thế nào nó có thể được thực hiện từ đầu. – Mars

+0

Ngoài ra, 'prewalk' yêu cầu một hàm phân biệt các nút lá từ các nút nhánh. Bạn không thể chỉ cho 'inc', ví dụ. – Mars

18

gì bạn mô tả là pr ecisely gì clojure.walk dành cho:

 
(def matrix [[1 2 3] 
      [4 5 6] 
      [7 8 9]]) 
(use 'clojure.walk :only [prewalk]) 
(prewalk #(if (number? %) (inc %) %) matrix) 
=> [[2 3 4] [5 6 7] [8 9 10]] 

Lưu ý 1: nó là thành ngữ để sử dụng vectơ thay vì dấu ngoặc đơn cho các bộ sưu tập tuần tự theo nghĩa đen.

Lưu ý 2: loại bảo quản đi bộ.

+0

Cảm ơn bạn đã đề cập đến Lưu ý 1 - Tôi vẫn còn hơi khó chịu khi sử dụng cái này hay cái kia. – Joel

+0

Câu trả lời này được tham chiếu bởi bảng gian lận Clojure tại clojure.org. – John

5

Kể từ sự ra đời của core.matrix vào năm 2013, điều này bây giờ là một cách tốt hơn xử lý hoạt động trên các mảng đa chiều:

(use 'clojure.core.matrix) 

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

(emap inc M) 

=> [[2 3 4 ] 
    [5 6 7 ] 
    [8 9 10]] 

Ưu điểm của việc sử dụng core.matrix:

  • sạch, thành ngữ Clojure mã
  • Rất nhiều chức năng thao tác mảng n chiều khác nhau - transpose, shape, reshape, slice, subarray v.v.
  • Khả năng cắm triển khai mảng hiệu suất cao (ví dụ: đối với mảng số lớn)
0

Câu trả lời muộn màng và có thể không chính xác những gì cần thiết: bạn có thể thử flatten. Nó sẽ trả về một seq mà bạn có thể lặp qua:

(flatten '((1 2 3) 
      (4 5 6) 
      (7 8 9))) 

user=> (1 2 3 4 5 6 7 8 9) 

Và để tăng yếu tố ma trận và lắp ráp lại các ma trận:

(partition 3 (map inc (flatten '((1 2 3) 
            (4 5 6) 
            (7 8 9))))) 
Các vấn đề liên quan