(defn do-to-map [amap keyseq f]
(reduce #(assoc %1 %2 (f (%1 %2))) amap keyseq))
Разбивка:
Это помогает смотреть на это наизнанку. В Clojure хеш-карты действуют как функции; если вы вызываете их как функцию с ключом в качестве аргумента, возвращается значение, связанное с этим ключом. Таким образом, с учетом одного ключа, текущее значение для этого ключа может быть получено через:
(some-map some-key)
Мы хотим взять старые значения и изменить их на новые, вызвав для них некоторую функцию f
. Таким образом, учитывая один ключ, новое значение будет:
(f (some-map some-key))
Мы хотим связать это новое значение с этим ключом в нашей хэш-карте, «заменив» старое значение. Вот что assoc
делает:
(assoc some-map some-key (f (some-map some-key)))
(«Заменить» в кавычках, потому что мы не мутируем ни один объект хэш-карты; мы возвращаем новые, неизменные, измененные объекты хэш-карты каждый раз, когда мы вызываем assoc
. Это все еще быстро и эффективен в Clojure, потому что хеш-карты являются постоянными и имеют общую структуру, когда вы assoc
их.)
Нам нужно многократно assoc
добавлять новые значения на нашу карту, по одному ключу за раз. Итак, нам нужна какая-то циклическая конструкция. Нам нужно начать с нашей исходной хэш-карты и одного ключа, а затем «обновить» значение этого ключа. Затем мы берем эту новую хэш-карту и следующий ключ и «обновляем» значение этого следующего ключа. И мы повторяем это для каждого ключа, по одному, и, наконец, возвращаем хэш-карту, которую мы «накопили». Это то, что reduce
делает.
- Первый аргумент
reduce
- это функция, которая принимает два аргумента: значение «аккумулятора», то есть значение, которое мы постоянно «обновляем» снова и снова; и один аргумент, используемый в одной итерации для накопления.
- Второй аргумент
reduce
- это начальное значение, переданное в качестве первого аргумента этому fn
.
- Третий аргумент
reduce
представляет собой набор аргументов, передаваемых в качестве второго аргумента этому fn
, по одному за раз.
Итак:
(reduce fn-to-update-values-in-our-map
initial-value-of-our-map
collection-of-keys)
fn-to-update-values-in-our-map
- это просто оператор assoc
сверху, завернутый в анонимную функцию:
(fn [map-so-far some-key] (assoc map-so-far some-key (f (map-so-far some-key))))
Так что подключите его к reduce
:
(reduce (fn [map-so-far some-key] (assoc map-so-far some-key (f (map-so-far some-key))))
amap
keyseq)
В Clojure есть сокращение для написания анонимных функций: #(...)
является анонимным fn
, состоящим из одной формы, в которой %1
связан с первым аргументом анонимной функции, %2
- с второе и т. д. Таким образом, наш fn
сверху может быть записан эквивалентно как
#(assoc %1 %2 (f (%1 %2)))
Это дает нам:
(reduce #(assoc %1 %2 (f (%1 %2))) amap keyseq)