10

我正在考虑 Clojure 中的状态。我来自可以改变状态的语言。例如,在 Python 中,我可以创建一个字典,将一些字符串 => 整数对放入其中,然后遍历字典并递增值。

我将如何在惯用的 Clojure 中做到这一点?

4

5 回答 5

12
(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)
于 2013-04-13T21:55:15.807 回答
3

只需生成一张新地图并使用它:

(def m {:a 3 :b 4})

(apply merge 
  (map (fn [[k v]] {k (inc v) }) m))

; {:b 5, :a 4}
于 2013-04-13T21:44:03.253 回答
2

要更新多个值,您还可以利用 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"}}
于 2013-04-13T22:41:07.127 回答
0

我一直在玩同样的想法,所以我想出了:

(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}
于 2014-02-19T21:57:18.707 回答
0

稍微改进@Michiel Brokent 的回答。如果密钥已经不存在,这将起作用。

(update my-map :a #(if (nil? %) 1 (inc %)))
于 2020-10-18T18:37:27.850 回答