克洛朱尔:如何将函数应用于哈希映射中的条目子集?
题
我没有使用 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"}
解决方案
(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
是作为第一个参数传递给 this 的初始值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)
其他提示
(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}
希望这会有所帮助。
以下似乎有效:
(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"}
可能有更好的方法来做到这一点。也许有人可以提出一个很好的单行:)