Я пытаюсь использовать функцию обновления Clojure, но я не могу понять, почему мне нужно передать функцию?
Как использовать "Обновление" в Clojure?
Ответ 1
update-in
выполняет функцию, поэтому вы можете обновлять значение в заданной позиции в зависимости от старого значения более кратко. Например, вместо:
(assoc-in m [list of keys] (inc (get-in m [list of keys])))
вы можете написать:
(update-in m [list of keys] inc)
Конечно, если новое значение не зависит от старого значения, assoc-in
является достаточным, и вам не нужно использовать update-in
.
Ответ 2
Это не прямой ответ на ваш вопрос, но одна из причин, по которым могла бы существовать функция типа update-in
, была бы для эффективности, а не просто для удобства — если бы она смогла обновить значение на карте "на месте". То есть вместо
- поиск ключа на карте,
- нахождение соответствующего ключевого значения кортежа,
- извлечение значения
- вычисление нового значения на основе текущего значения,
- поиск ключа на карте,
- нахождение соответствующего ключевого значения кортежа,
- и перезаписать значение в кортеже или заменить кортеж на новый
можно представить себе алгоритм, который бы опустил второй поиск ключа:
- найдите ключ на карте,
- найдите соответствующий набор значений ключа,
- извлеките значение,
- вычислить новое значение, основанное на текущем значении,
- и перезаписать значение в кортеже
К сожалению, текущая реализация update-in
не выполняет это обновление "на месте". Он использует get
для извлечения и assoc
для замены. Если assoc
не использует кеширование последнего просматриваемого ключа и соответствующего кортежа для ключевого значения, вызов assoc
завершается, чтобы снова искать ключ.
Ответ 3
Я думаю, что короткий ответ заключается в том, что функция, переданная в update-in, позволяет обновлять значения за один шаг, а не 3 (искать, вычислять новое значение, устанавливать).
Кстати, только сегодня я столкнулся с этим использованием update-in в презентации Clojure от Howard Lewis Ship:
(def in-str "this is this")
(reduce
(fn [m k] (update-in m [k] #(inc (or % 0))))
{}
(seq in-str))
==> {\space 2, \s 3, \i 3, \h 2, \t 2}
Каждый вызов update-in берет букву в виде ключа, просматривает ее на карте, и если она там обнаруживает, увеличивается количество букв (иначе устанавливается значение 1). Сокращение приводит процесс, начиная с пустой карты {} и повторно применяя обновление с последовательными символами из входной строки. В результате получается карта буквенных частот. Slick.
Примечание 1: clojure.core/частоты аналогичны, но использует связь! а не обновление.
Примечание 2: Вы можете заменить # (inc (или% 0)) на (fnil inc 0). Отсюда: fnil
Ответ 4
Практический пример: здесь.
Введите этот фрагмент (в вашем REPL):
(def my-map {:useless-key "key"})
;;{:useless-key "key"}
(def my-map (update-in my-map [:yourkey] #(cons 1 %)))
;;{:yourkey (1), :useless-key "key"}
Обратите внимание, что :yourkey
является новым. Таким образом, значение - :yourkey
- передается лямбда null
. cons
будет помещать 1
в качестве единственного элемента вашего списка. Теперь сделайте следующее:
(def my-map (update-in my-map [:yourkey] #(cons 25 %)))
;;{:yourkey (25 1), :useless-key "key"}
И это во второй части, анонимная функция принимает список - значение для: yourkey - в качестве аргумента и только минус 25.
Так как наш my-map
неизменен, update-in
всегда будет возвращать новую версию вашей карты, позволяя вам что-то делать со старым значением данного ключа.
Надеюсь, что это помогло!