如何在Clojure中使用“Update-in”?

时间:2011-01-09 21:32:35

标签: clojure

我正在尝试使用Clojure的update-in函数,但我似乎无法理解为什么需要传入函数?

4 个答案:

答案 0 :(得分:30)

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

答案 1 :(得分:5)

这不是你问题的直接答案,但是像update-in这样的函数可能存在的一个原因是为了效率 - 而不仅仅是方便 - 如果它能够更新地图中的值“ -地点”。也就是说,而不是

  • 在地图上寻找钥匙,
  • 找到相应的键值元组,
  • 提取值,
  • 根据当前值计算新值
  • 在地图上寻找钥匙,
  • 找到相应的键值元组,
  • 并覆盖元组中的值或用新的元素替换元组

可以想象一种算法可以省略对密钥的第二次搜索:

  • 在地图中寻找钥匙,
  • 找到相应的键值元组,
  • 提取值,
  • 根据当前值计算新值
  • 并覆盖元组中的值

不幸的是,the current implementation of update-in没有执行此“就地”更新。它使用get进行提取,使用assoc进行替换。除非assoc正在使用最后一个查找键和相应的键值元组的某些缓存,否则对assoc的调用将不得不再次寻找密钥。

答案 2 :(得分:3)

我认为简短的回答是传递给update-in的函数允许您在一个步骤中更新值,而不是3(查找,计算新值,设置)。

巧合的是,就在今天,我在Howard Lewis Ship的Clojure演讲中遇到过update-in的使用:

(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)。 reduce通过以空映射{}开始驱动进程,并使用输入字符串中的连续字符重复应用update-in。结果是字母频率图。光滑。

注1:clojure.core /频率相似但使用了assoc!而不是更新。

注2:您可以用(fnil inc 0)替换#(inc(或%0))。从这里开始:fnil

答案 3 :(得分:1)

您看到here的实际示例。

输入此代码段(在您的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是新的。因此传递给lambda的:yourkey - 的值是nullcons会将1作为您列表中的单个元素。现在执行以下操作:

(def my-map (update-in my-map [:yourkey] #(cons 25 %)))
;;{:yourkey (25 1), :useless-key "key"}

就是这样,在第二部分中,匿名函数将列表 - 值为:yourkey - 作为参数,只是对它有利。

由于我们的my-map是不可变的,update-in将始终返回您的地图的新版本,让您使用给定密钥的旧值执行某些操作。

希望它有所帮助!