Clojure:如何将函数应用于哈希映射中的条目子集?

时间:2009-10-28 17:37:14

标签: map clojure hashmap

我不是要Clojure并试图弄清楚如何做到这一点。

我想创建一个新的哈希映射,哈希映射中的一个键子集将一个函数应用于元素。这样做的最佳方式是什么?

(let 
   [my-map {:hello "World" :try "This" :foo "bar"}]
   (println (doToMap my-map [:hello :foo] (fn [k] (.toUpperCase k)))

这应该会产生类似

的地图
{:hello "WORLD" :try "This" :foo "BAR"}

3 个答案:

答案 0 :(得分:24)

(defn do-to-map [amap keyseq f]
  (reduce #(assoc %1 %2 (f (%1 %2))) amap keyseq))

故障:

从内到外看是有帮助的。在Clojure中,哈希映射就像函数一样;如果将它们称为具有键作为参数的函数,则返回与该键关联的值。因此,给定一个密钥,该密钥的当前值可以通过以下方式获得:

(some-map some-key)

我们希望获取旧值,并通过调用它们上的某个函数f将它们更改为新值。因此,只需一个键,新值将为:

(f (some-map some-key))

我们希望在哈希映射中将此新值与此键相关联,“替换”旧值。这就是assoc的作用:

(assoc some-map some-key (f (some-map some-key)))

(“替换”是恐吓引用,因为我们没有改变单个散列映射对象;每次调用assoc时,我们都会返回新的,不可变的,更改的散列映射对象。这是在Clojure中仍然快速有效,因为当你assoc时,哈希映射是持久的并且共享结构。)

我们需要在地图上重复assoc个新值,一次一个键。所以我们需要某种循环结构。我们想要的是从我们的原始哈希映射和单个键开始,然后“更新”该键的值。然后我们采用新的哈希映射和下一个键,并“更新”该下一个键的值。我们为每个键重复一次,一次一个,最后返回我们“累积”的哈希映射。这就是reduce的作用。

  • reduce的第一个参数是一个带有两个参数的函数:一个“累加器”值,它是我们一遍又一遍地“更新”的值;并且在一次迭代中使用一个参数来完成一些累积。
  • reduce的第二个参数是作为此fn的第一个参数传递的初始值。
  • reduce的第三个参数是要作为第fn个第二个参数传递的参数集合,一次一个。

所以:

(reduce fn-to-update-values-in-our-map 
        initial-value-of-our-map 
        collection-of-keys)

fn-to-update-values-in-our-map只是上面的assoc语句,包含在一个匿名函数中:

(fn [map-so-far some-key] (assoc map-so-far some-key (f (map-so-far some-key))))

然后将其插入reduce

(reduce (fn [map-so-far some-key] (assoc map-so-far some-key (f (map-so-far some-key))))
        amap
        keyseq)

在Clojure中,有一个写匿名函数的简写:#(...)是一个由单个表单组成的匿名fn,其中%1绑定到匿名函数的第一个参数,%2到第二个,等等。所以我们上面的fn可以等同地写成:

#(assoc %1 %2 (f (%1 %2)))

这给了我们:

(reduce #(assoc %1 %2 (f (%1 %2))) amap keyseq)

答案 1 :(得分:4)

(defn doto-map [m ks f & args]
  (reduce #(apply update-in %1 [%2] f args) m ks))

示例电话

user=> (doto-map {:a 1 :b 2 :c 3} [:a :c] + 2)
{:a 3, :b 2, :c 5}

希望这会有所帮助。

答案 2 :(得分:1)

以下似乎有效:

(defn doto-map [ks f amap]
  (into amap
    (map (fn [[k v]] [k (f v)])
         (filter (fn [[k v]] (ks k)) amap))))

user=> (doto-map #{:hello :foo} (fn [k] (.toUpperCase k)) {:hello "World" :try "This" :foo "bar"})
{:hello "WORLD", :try "This", :foo "BAR"}

可能有更好的方法来做到这一点。也许有人可以提出一个很好的单行:)