Сопоставление функции со значениями map в Clojure
-
16-09-2019 - |
Вопрос
Я хочу преобразовать одну карту значений в другую карту с теми же ключами, но с функцией, примененной к значениям.Я бы подумал, что в clojure api есть функция для этого, но я не смог ее найти.
Вот пример реализации того, что я ищу
(defn map-function-on-map-vals [m f]
(reduce (fn [altered-map [k v]] (assoc altered-map k (f v))) {} m))
(println (map-function-on-map-vals {:a "test" :b "testing"} #(.toUpperCase %)))
{:b TESTING, :a TEST}
Кто-нибудь знает, если map-function-on-map-vals
уже существует?Я бы подумал, что так оно и было (возможно, и с более приятным названием).
Решение
мне нравится твой reduce
версия просто отлично.Я думаю, это идиоматично.В любом случае вот версия, использующая понимание списка.
(defn foo [m f]
(into {} (for [[k v] m] [k (f v)])))
Другие советы
Вы можете использовать clojure.algo.generic.functor/fmap
:
user=> (use '[clojure.algo.generic.functor :only (fmap)])
nil
user=> (fmap inc {:a 1 :b 3 :c 5})
{:a 2, :b 4, :c 6}
Вот довольно типичный способ трансформации карты. zipmap
берет список ключей и список значений и «делает правильные вещи», создавая новую карту Clojure.Вы также можете поставить map
вокруг клавиш, чтобы изменить их, или и то, и другое.
(zipmap (keys data) (map #(do-stuff %) (vals data)))
или обернуть его в свою функцию:
(defn map-function-on-map-vals [m f]
(zipmap (keys m) (map f (vals m))))
Из «Книги рецептов Clojure» есть сокращение-kv:
(defn map-kv [m f]
(reduce-kv #(assoc %1 %2 (f %3)) {} m))
Вот довольно идиоматический способ сделать это:
(defn map-function-on-map-vals [m f]
(apply merge
(map (fn [[k v]] {k (f v)})
m)))
Пример:
user> (map-function-on-map-vals {1 1, 2 2, 3 3} inc))
{3 4, 2 3, 1 2}
map-map
, map-map-keys
, и map-map-values
Я не знаю ни одной существующей функции в Clojure для этого, но вот реализация этой функции как map-map-values
который вы можете свободно копировать.Он поставляется с двумя тесно связанными функциями: map-map
и map-map-keys
, которые также отсутствуют в стандартной библиотеке:
(defn map-map
"Returns a new map with each key-value pair in `m` transformed by `f`. `f` takes the arguments `[key value]` and should return a value castable to a map entry, such as `{transformed-key transformed-value}`."
[f m]
(into (empty m) (map #(apply f %) m)) )
(defn map-map-keys [f m]
(map-map (fn [key value] {(f key) value}) m) )
(defn map-map-values [f m]
(map-map (fn [key value] {key (f value)}) m) )
Применение
Вы можете позвонить map-map-values
так:
(map-map-values str {:a 1 :b 2})
;; => {:a "1", :b "2"}
А две другие функции такие:
(map-map-keys str {:a 1 :b 2})
;; => {":a" 1, ":b" 2}
(map-map (fn [k v] {v k}) {:a 1 :b 2})
;; => {1 :a, 2 :b}
Альтернативные реализации
Если ты хочешь только map-map-keys
или map-map-values
, без более общего map-map
функцию, вы можете использовать эти реализации, которые не полагаются на map-map
:
(defn map-map-keys [f m]
(into (empty m)
(for [[key value] m]
{(f key) value} )))
(defn map-map-values [f m]
(into (empty m)
(for [[key value] m]
{key (f value)} )))
Кроме того, вот альтернативная реализация map-map
это основано на clojure.walk/walk
вместо into
, если вам нравится эта формулировка:
(defn map-map [f m]
(clojure.walk/walk #(apply f %) identity m) )
Параллельные версии – pmap-map
, и т. д.
Также существуют параллельные версии этих функций, если они вам нужны.Они просто используют pmap
вместо map
.
(defn pmap-map [f m]
(into (empty m) (pmap #(apply f %) m)) )
(defn pmap-map-keys [f m]
(pmap-map (fn [key value] {(f key) value}) m) )
(defn pmap-map-values [f m]
(pmap-map (fn [key value] {key (f value)}) m) )
Я использую Clojure n00b, так что вполне могут быть гораздо более элегантные решения.Вот мой:
(def example {:a 1 :b 2 :c 3 :d 4})
(def func #(* % %))
(prn example)
(defn remap [m f]
(apply hash-map (mapcat #(list % (f (% m))) (keys m))))
(prn (remap example func))
Функция anon создает небольшой двухсписочный список из каждого ключа и его заданного значения.Mapcat запускает эту функцию над последовательностью клавиш карты и объединяет все работы в один большой список."применить хэш-карту" создает новую карту из этой последовательности.(% m) может выглядеть немного странно, это идиоматический Clojure для применения ключа к карте для поиска связанного значения.
Самое настоятельно рекомендуемое чтение:Тот Самый Шпаргалка по Clojure .
Мне нравится твой reduce
версия.С очень незначительными изменениями он также может сохранять тип структур записей:
(defn map-function-on-map-vals [m f]
(reduce (fn [altered-map [k v]] (assoc altered-map k (f v))) m m))
Тот Самый {}
был заменен на m
.С этим изменением записи остаются записями:
(defrecord Person [firstname lastname])
(def p (map->Person {}))
(class p) '=> Person
(class (map-function-on-map-vals p
(fn [v] (str v)))) '=> Person
Начав с {}
, запись теряет свою рекордность, который, возможно, захочется сохранить, если вам нужны возможности записи (например, компактное представление в памяти).
(defn map-vals
"Map f over every value of m.
Returns a map with the same keys as m, where each of its values is now the result of applying f to them one by one.
f is a function of one arg, which will be called which each value of m, and should return the new value.
Faster then map-vals-transient on small maps (8 elements and under)"
[f m]
(reduce-kv (fn [m k v]
(assoc m k (f v)))
{} m))
(defn map-vals-transient
"Map f over every value of m.
Returns a map with the same keys as m, where each of its values is now the result of applying f to them one by one.
f is a function of one arg, which will be called which each value of m, and should return the new value.
Faster then map-vals on big maps (9 elements or more)"
[f m]
(persistent! (reduce-kv (fn [m k v]
(assoc! m k (f v)))
(transient {}) m)))