Подтвердить что ты не робот

Как использовать "Обновление" в Clojure?

Я пытаюсь использовать функцию обновления Clojure, но я не могу понять, почему мне нужно передать функцию?

4b9b3361

Ответ 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 всегда будет возвращать новую версию вашей карты, позволяя вам что-то делать со старым значением данного ключа.

Надеюсь, что это помогло!