2011-09-02 31 views
8

Tôi rất mới với Clojure và có một vấn đề thú vị đối với bạn rất kinh nghiệm Clojure. Tôi đang làm việc thông qua cuốn sách "Lập trình tập thể dục thông minh" và cố gắng để mã các ví dụ trong Clojure (cuốn sách có tất cả trong Python). Trong chương đầu tiên, chúng tôi có một thiết lập bản đồ băm của các nhà phê bình phim và thứ hạng mà họ đã đưa ra cho các bộ phim khác nhau. Có vẻ như sau:Bật bản đồ băm bên trong trong Clojure

{"Lisa Rose" {"Lady in the Water" 2.5, "Snakes on a Plane" 3.5 }, 
"Gene Seymour" {"Lady in the Water" 3.0, "Snakes on a Plane" 3.5}} 

Vấn đề là vấn đề này. Làm thế nào để biến điều đó ra bên trong để tôi có được một bản đồ băm trông giống như thế này:

{"Lady in the Water" {"Lisa Rose" 2.5, "Gene Seymour" 3.0}, 
"Snakes on a Plane" {"Lisa Rose" 3.5, "Gene Seymour" 3.5}} 

Chức năng của bạn để thực hiện điều này là gì?

+0

Cảm ơn tất cả các câu trả lời tuyệt vời. Tôi đã học được điều gì đó từ tất cả chúng. –

Trả lời

17
(let [m {"Lisa Rose" {"Lady in the Water" 2.5, "Snakes on a Plane" 3.5 }, 
     "Gene Seymour" {"Lady in the Water" 3.0, "Snakes on a Plane" 3.5}}] 
    (apply merge-with merge 
     (for [[ok ov] m 
       [ik iv] ov] 
      {ik {ok iv}}))) 

{"Snakes on a Plane" {"Gene Seymour" 3.5, "Lisa Rose" 3.5}, 
"Lady in the Water" {"Gene Seymour" 3.0, "Lisa Rose" 2.5}} 
+0

Cảm ơn bạn đã đánh dấu [câu hỏi] của tôi (http://stackoverflow.com/q/23651045/813665) là trùng lặp. Bạn có nghĩ rằng tốt hơn nên sử dụng 'conj' hoặc' merge' cho 'merge-with'? – viebel

+0

@viebel Tôi thích 'hợp nhất'.Tôi không thích dựa vào thực tế kỳ lạ là đối với bản đồ, 'conj' tương đương với' thành' - tôi thích giả vờ rằng 'conj' chỉ chấp nhận các mục bản đồ. – amalloy

3

Tôi có thể đề xuất sau: Chúng tôi có bản đồ - tập hợp các mục nhập. Rẽ mỗi entry trong ra ngoài và họ kết hợp chúng: ban đầu:

{:Lisa {:Lady 2.5, :Snakes 3.5}, 
:Gene {:Lady 3.0, :Snakes 3.5}} 

Inverse mỗi entry:

([:Lady {:Lisa 2.5}], [:Snakes {:Lisa 3.5}]) 
([:Lady {:Gene 3.0}], [:Snakes {:Gene 3.5}]) 

concat họ:

([:Lady {:Lisa 2.5}], [:Snakes {:Lisa 3.5}], [:Lady {:Gene 3.0}], [:Snakes {:Gene 3.5}]) 

Và họ kết hợp chúng với một bản đồ:

{:Lady {:Lisa 2.5, :Gene 3.0}, 
:Snakes {:Lisa 3.5, :Gene 3.5}} 

Code:

(defn inverse-map [m] 
     (let [inverse-entry (fn [[name movies]] 
           (map (fn [[movie rating]] 
             [movie {name rating}]) 
            movies))] 
      (->> (map inverse-entry m) 
       (reduce concat) 
       (reduce (fn [res [movie entry]] 
          (update-in res [movie] merge entry)) 
         {})))) 

Vì vậy, chúng tôi nhận bản đồ, (nó là tập hợp các vectơ [giá trị key]), ngược lại mỗi entry (vector [chính trị]). Bây giờ chúng tôi có bộ sưu tập các bộ sưu tập của vectơ, concat chúng vào một bộ sưu tập. Và cuối cùng, bằng cách sử dụng giảm, chúng tôi thêm mỗi véc tơ vào bản đồ.

Tôi cho rằng có giải pháp thanh lịch hơn, nhưng tôi cũng hoạt động.

6
(defn inverse-map [m] 
    (let [inner-keys (-> m first val keys) 
     outer-keys (keys m)] 
    (apply merge-with merge 
      (for [ik inner-keys 
       ok outer-keys] 
      {ik {ok (get-in input [ok ik])}})))) 

Giả định rằng tất cả các phím quan tâm trong bản đồ bên trong đều có trên bản đồ bên trong đầu tiên. Nếu điều này không đúng, (-> m first val keys) sẽ phải được thay thế bằng thứ gì đó trả về bộ sưu tập tất cả các khóa quan tâm, ví dụ: (->> m (map values) (mapcat keys)).

Ý tưởng là xây dựng bản đồ dạng {inner-key {outer-key the-value-at-inner-key-in-the-map-at-outer-key}}, sau đó hợp nhất chúng lại với nhau một cách thích hợp.

Giá trị trả lại trên bản đồ trong văn bản câu hỏi được chỉ định.

Bây giờ, ở trên tạo ra rất nhiều cấu trúc trung gian, có thể là một vấn đề hiệu suất-khôn ngoan. Nếu tốc độ là điều cốt yếu, bạn có thể chuyển sang loop và transients:

(defn transient-inverse-map [m] 
    (let [inner-keys (-> m first val keys) 
     outer-keys (keys m) 
     t (transient {})] 
    (loop [inner-keys inner-keys 
      t t] 
     (if (seq inner-keys) 
     (recur (next inner-keys) 
       (assoc! t ik 
         (let [ik (first inner-keys) 
          t (transient {})] 
         (loop [outer-keys outer-keys 
           t t] 
          (if (seq outer-keys) 
          (let [ok (first outer-keys)] 
           (recur (next outer-keys) 
             (assoc! t ok (get-in m [ok ik])))) 
          (persistent! t)))))) 
     (persistent! t))))) 

Ý tưởng là như nhau, nếu khó khăn hơn để phân biệt từ mã.

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