我没有使用 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"}

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top