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

Jer*_*rks 5 clojure hashmap map

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

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

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

这应该会产生类似的地图

{:hello "WORLD" :try "This" :foo "BAR"}
Run Code Online (Sandbox Code Playgroud)

Bri*_*per 24

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

分解:

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

(some-map some-key)
Run Code Online (Sandbox Code Playgroud)

我们想要获取旧值,并通过调用f它们上的某个函数将它们更改为新值.所以给定一个键,新值将是:

(f (some-map some-key))
Run Code Online (Sandbox Code Playgroud)

我们希望在哈希映射中将此新值与此键相关联,"替换"旧值.这是做什么的assoc:

(assoc some-map some-key (f (some-map some-key)))
Run Code Online (Sandbox Code Playgroud)

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

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

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

所以:

(reduce fn-to-update-values-in-our-map 
        initial-value-of-our-map 
        collection-of-keys)
Run Code Online (Sandbox Code Playgroud)

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))))
Run Code Online (Sandbox Code Playgroud)

所以插入reduce:

(reduce (fn [map-so-far some-key] (assoc map-so-far some-key (f (map-so-far some-key))))
        amap
        keyseq)
Run Code Online (Sandbox Code Playgroud)

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

#(assoc %1 %2 (f (%1 %2)))
Run Code Online (Sandbox Code Playgroud)

这给了我们:

(reduce #(assoc %1 %2 (f (%1 %2))) amap keyseq)
Run Code Online (Sandbox Code Playgroud)