我正在考虑 Clojure 中的状态。我来自可以改变状态的语言。例如,在 Python 中,我可以创建一个字典,将一些字符串 => 整数对放入其中,然后遍历字典并递增值。
我将如何在惯用的 Clojure 中做到这一点?
我正在考虑 Clojure 中的状态。我来自可以改变状态的语言。例如,在 Python 中,我可以创建一个字典,将一些字符串 => 整数对放入其中,然后遍历字典并递增值。
我将如何在惯用的 Clojure 中做到这一点?
(def my-map {:a 1 :b 2})
(zipmap (keys my-map) (map inc (vals my-map)))
;;=> {:b 3, :a 2}
按键只更新一个值:
(update-in my-map [:b] inc) ;;=> {:a 1, :b 3}
从 Clojure 1.7 开始,也可以使用update
:
(update my-map :b inc)
只需生成一张新地图并使用它:
(def m {:a 3 :b 4})
(apply merge
(map (fn [[k v]] {k (inc v) }) m))
; {:b 5, :a 4}
要更新多个值,您还可以利用 reduce 获取已填充的累加器,并在该累加器和以下集合的每个成员上应用一个函数。
=> (reduce (fn [a k] (update-in a k inc)) {:a 1 :b 2 :c 3 :d 4} [[:a] [:c]])
{:a 2, :c 4, :b 2, :d 4}
请注意需要包含在向量中的键,但您仍然可以在嵌套结构中执行多个更新,如原始更新。
如果你把它变成一个通用函数,你可以通过用 coll? 测试它来自动将一个向量包裹在一个键上:
(defn multi-update-in
[m v f & args]
(reduce
(fn [acc p] (apply
(partial update-in acc (if (coll? p) p (vector p)) f)
args)) m v))
这将允许单级/密钥更新,而无需将密钥包装在向量中
=> (multi-update-in {:a 1 :b 2 :c 3 :d 4} [:a :c] inc)
{:a 2, :c 4, :b 2, :d 4}
但仍然可以进行嵌套更新
(def people
{"keith" {:age 27 :hobby "needlefelting"}
"penelope" {:age 39 :hobby "thaiboxing"}
"brian" {:age 12 :hobby "rocket science"}})
=> (multi-update-in people [["keith" :age] ["brian" :age]] inc)
{"keith" {:age 28, :hobby "needlefelting"},
"penelope" {:age 39, :hobby "thaiboxing"},
"brian" {:age 13, :hobby "rocket science"}}
我一直在玩同样的想法,所以我想出了:
(defn remap
"returns a function which takes a map as argument
and applies f to each value in the map"
[f]
#(into {} (map (fn [[k v]] [k (f v)]) %)))
((remap inc) {:foo 1})
;=> {:foo 2}
或者
(def inc-vals (remap inc))
(inc-vals {:foo 1})
;=> {:foo 2}
稍微改进@Michiel Brokent 的回答。如果密钥已经不存在,这将起作用。
(update my-map :a #(if (nil? %) 1 (inc %)))