【问题标题】:How to Increment Values in a Map如何增加地图中的值
【发布时间】:2013-04-06 06:29:03
【问题描述】:

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

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

【问题讨论】:

    标签: clojure state immutability


    【解决方案1】:
    (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)
    

    【讨论】:

      【解决方案2】:

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

      (def m {:a 3 :b 4})
      
      (apply merge 
        (map (fn [[k v]] {k (inc v) }) m))
      
      ; {:b 5, :a 4}
      

      【讨论】:

      • 一个值怎么样?那么如果我想要 {a: 7 :b 4} 怎么办。如果我有一个整数映射图并且想要增加say key1 => subkey1 => integer++ 的值怎么办?
      • 知道了,这会创建一个新地图,对吗?我认为这使保持状态变得更加困难。例如,要创建贝叶斯分类器,我需要在多级结构中不断更新事物的计数。 key1 => subkey1 => 整数。我需要做 som dosync 和交换魔法吗?
      • 如果您在多线程环境(如 Web 应用程序)中执行此操作,请务必小心。您必须使用 Clojure 的 STM 或类似 stackoverflow.com/questions/7461562/… 的东西。如果您使用 Java 或想要 Java 解决方案 Guava 的 AtomicLongMap 负责并发计数器。
      【解决方案3】:

      要更新多个值,您还可以利用 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"}}
      

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          【解决方案5】:

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

          (update my-map :a #(if (nil? %) 1 (inc %)))
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-02-06
            • 1970-01-01
            • 2021-05-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多