Map Clojure:如何将函数应用于哈希映射中的一个子集?

Map Clojure:如何将函数应用于哈希映射中的一个子集?,map,clojure,hashmap,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 "

我并不是要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 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"}
也许有更好的方法可以做到这一点。也许有人能想出一句漂亮的一句话:)

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

(f (some-map some-key))
我们希望将这个新值与哈希映射中的这个键相关联,“替换”旧值。这就是
assoc
所做的:

(assoc some-map some-key (f (some-map some-key)))
(“Replace”在短引号中,因为我们没有变异单个哈希映射对象;每次调用
assoc
时,我们都返回新的、不可变的、修改过的哈希映射对象。这在Clojure中仍然是快速有效的,因为当调用
assoc
时,哈希映射是持久的,并且共享结构。)

我们需要反复地将新值关联到地图上,每次一个键。所以我们需要某种循环结构。我们想要的是从原始哈希映射和单个键开始,然后“更新”该键的值。然后我们获取新的哈希映射和下一个键,并“更新”下一个键的值。我们对每个键重复这个过程,一次一个,最后返回我们“累积”的哈希映射。这就是
reduce
所做的

  • reduce
    的第一个参数是一个接受两个参数的函数:一个“累加器”值,它是我们不断“更新”的值;在一次迭代中使用一个参数来做一些累加
  • reduce
    的第二个参数是作为第一个参数传递给该
    fn
    的初始值
  • reduce
    的第三个参数是作为第二个参数传递到此
    fn
    的参数集合,一次一个
因此:

fn更新地图中的值
只是上面的
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)
示例调用

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

希望这能有所帮助。

您介意为我们这些刚接触Clojure的人逐一描述一下吗?那个#(…)符号太棒了。回答得好!回答得很好。感谢你一步一步地走过它!
#(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}