Переверните хеш-карту наизнанку в Clojure - PullRequest
8 голосов
/ 03 сентября 2011

Я очень новичок в Clojure и у меня есть интересная проблема для вас, гуру Clojure.Я работаю над книгой «Программирование коллективного интеллекта» и пытаюсь закодировать примеры в Clojure (в книге все они написаны на Python).В первой главе у нас есть настройка хеш-карты кинокритиков и рейтинги, которые они дали различным фильмам.Это выглядит так:

{"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}}

Проблема заключается в следующем.Как вывернуть это наизнанку, чтобы я получил хеш-карту, которая выглядит следующим образом:

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

Какова будет ваша функция для достижения этой цели?

Ответы [ 3 ]

17 голосов
/ 03 сентября 2011
(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}}
6 голосов
/ 03 сентября 2011
(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])}}))))

Предполагается, что все ключи, представляющие интерес для внутренних карт, присутствуют на первой внутренней карте. Если это неверно, (-> m first val keys) придется заменить чем-то, возвращающим набор всех ключей, представляющих интерес, например, (->> m (map values) (mapcat keys)).

Идея состоит в том, чтобы построить карту вида {inner-key {outer-key the-value-at-inner-key-in-the-map-at-outer-key}}, а затем объединить их соответствующим образом.

Возвращаемое значение на карте в тексте вопроса соответствует указанному.

Теперь вышеприведенное создает много промежуточных структур, что может быть проблемой с точки зрения производительности. Если скорость важна, вы можете переключиться на loop и переходные процессы:

(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)))))

Идея та же, если ее сложнее разглядеть в коде.

3 голосов
/ 03 сентября 2011

Могу предложить следующее: У нас есть карта - коллекция записей.Переверните каждую запись наизнанку, и они объединят их: По умолчанию:

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

Инвертируйте каждую запись:

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

Конкатируйте их:

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

И они объединят ихна одну карту:

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

Код:

(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))
                        {}))))

Таким образом, мы получаем карту (это набор векторов [значение ключа]), инвертирующий каждую запись (вектор [значение ключа]).Теперь у нас есть коллекция коллекций векторов, объединяющих их в одну коллекцию.И, наконец, используя Reduce, мы добавляем каждый вектор на карту.

Полагаю, есть более элегантное решение, но мое тоже работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...