Как использовать «Обновление» в Clojure? - PullRequest
16 голосов
/ 10 января 2011

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

Ответы [ 4 ]

30 голосов
/ 10 января 2011

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.

5 голосов
/ 10 января 2011

Это не прямой ответ на ваш вопрос, но одна из причин, по которой может существовать такая функция, как update-in, заключается в эффективности & mdash; не просто в удобстве & mdash; если бы она могла обновлять значение на карте "на месте" ». То есть, а не

  • ищет ключ на карте,
  • найти соответствующий кортеж ключ-значение,
  • извлечение значения,
  • вычисление нового значения на основе текущего значения,
  • ищет ключ на карте,
  • найти соответствующий кортеж ключ-значение,
  • и перезаписать значение в кортеже или заменить кортеж новым

вместо этого можно представить алгоритм, который бы пропустил второй поиск ключа:

  • найдите ключ на карте,
  • найти соответствующий кортеж ключ-значение,
  • извлечь значение,
  • вычислить новое значение на основе текущего значения,
  • и перезаписать значение в кортеже

К сожалению, текущая реализация update-in не делает это обновление "на месте". Для извлечения используется get и assoc для замены. Если assoc не использует некоторое кэширование последнего поискового ключа и соответствующего кортежа значения ключа, вызов assoc завершает необходимость поиска ключа снова.

3 голосов
/ 11 января 2011

Я думаю, что короткий ответ заключается в том, что функция, переданная для обновления, позволяет обновлять значения за один шаг, а не за 3 (поиск, вычисление нового значения, установка).

По совпадению, только сегодня ястолкнулся с этим использованием обновления в презентации Clojure Говарда Льюиса Шипа:

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

Каждый вызов обновления принимает букву в качестве ключа, ищет ее на карте и, если она найденатам увеличивается счетчик букв (в противном случае устанавливается значение 1).Reduce управляет процессом, начиная с пустой карты {}, и неоднократно применяет обновление с последовательными символами из входной строки.Результатом является карта частот букв.Slick.

Примечание 1: clojure.core / частоты похожа, но использует ассоциацию!вместо обновления.

Примечание 2: Вы можете заменить # (inc (или% 0)) на (fnil inc 0).Отсюда: ФНИЛ

1 голос
/ 10 января 2011

Практический пример, который вы видите здесь .

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

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

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