简体   繁体   English

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

[英]Clojure: How do I apply a function to a subset of the entries in a hash-map?

I am not to Clojure and attempting to figure out how to do this. 我不是要Clojure并试图弄清楚如何做到这一点。

I want to create a new hash-map that for a subset of the keys in the hash-map applies a function to the elements. 我想创建一个新的哈希映射,哈希映射中的一个键子集将一个函数应用于元素。 What is the best way to do this? 做这个的最好方式是什么?

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

This should then result a map with something like 这应该会产生类似的地图

{:hello "WORLD" :try "This" :foo "BAR"}
(defn do-to-map [amap keyseq f]
  (reduce #(assoc %1 %2 (f (%1 %2))) amap keyseq))

Breakdown: 分解:

It helps to look at it inside-out. 它有助于从内到外看。 In Clojure, hash-maps act like functions; 在Clojure中,哈希映射就像函数一样; if you call them like a function with a key as an argument, the value associated with that key is returned. 如果将它们称为具有键作为参数的函数,则返回与该键关联的值。 So given a single key, the current value for that key can be obtained via: 因此,只需一个密钥,就可以通过以下方式获得该密钥的当前值:

(some-map some-key)

We want to take old values, and change them to new values by calling some function f on them. 我们想要获取旧值,并通过调用它们上的某个函数f将它们更改为新值。 So given a single key, the new value will be: 所以给定一个键,新值将是:

(f (some-map some-key))

We want to associate this new value with this key in our hash-map, "replacing" the old value. 我们希望在哈希映射中将此新值与此键相关联,“替换”旧值。 This is what assoc does: 这就是assoc所做的:

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

("Replace" is in scare-quotes because we're not mutating a single hash-map object; we're returning new, immutable, altered hash-map objects each time we call assoc . This is still fast and efficient in Clojure because hash-maps are persistent and share structure when you assoc them.) (“替换”是恐吓引用,因为我们没有改变单个散列映射对象;每次调用assoc时,我们都会返回新的,不可变的,改变的散列映射对象。这在Clojure中仍然快速有效,因为哈希映射是持久的,并在assoc它们时共享结构。)

We need to repeatedly assoc new values onto our map, one key at a time. 我们需要重复将新值assoc到地图上,一次一个键。 So we need some kind of looping construct. 所以我们需要某种循环结构。 What we want is to start with our original hash-map and a single key, and then "update" the value for that key. 我们想要的是从我们的原始哈希映射和单个键开始,然后“更新”该键的值。 Then we take that new hash-map and the next key, and "update" the value for that next key. 然后我们采用新的哈希映射和下一个键,并“更新”该下一个键的值。 And we repeat this for every key, one at a time, and finally return the hash-map we've "accumulated". 我们为每个键重复一次,一次一个,最后返回我们“累积”的哈希映射。 This is what reduce does. 这就是reduce

  • The first argument to reduce is a function that takes two arguments: an "accumulator" value, which is the value we keep "updating" over and over; reduce的第一个参数是一个带有两个参数的函数:一个“累加器”值,它是我们一遍又一遍地“更新”的值; and a single argument used in one iteration to do some of the accumulating. 并且在一次迭代中使用一个参数来完成一些累积。
  • The second argument to reduce is the initial value passed as the first argument to this fn . reduce的第二个参数是作为此fn的第一个参数传递的初始值。
  • The third argument to reduce is a collection of arguments to be passed as the second argument to this fn , one at a time. reduce的第三个参数是一个参数集合,它作为第二个参数传递给这个fn ,一次一个。

So: 所以:

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

fn-to-update-values-in-our-map is just the assoc statement from above, wrapped in an anonymous function: 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))))

So plugging it into reduce : 因此将其插入reduce

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

In Clojure, there's a shorthand for writing anonymous functions: #(...) is an anonymous fn consisting of a single form, in which %1 is bound to the first argument to the anonymous function, %2 to the second, etc. So our fn from above can be written equivalently as: 在Clojure中,有一个写匿名函数的简写: #(...)是一个由单个表单组成的匿名fn ,其中%1绑定到匿名函数的第一个参数, %2绑定到第二个参数,等等。所以我们上面的fn可以写成:

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

This gives us: 这给了我们:

(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))

Example call 示例电话

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

Hopes this helps. 希望这会有所帮助。

The following seems to work: 以下似乎有效:

(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"}

There might be a better way to do this. 可能有更好的方法来做到这一点。 Perhaps someone can come up with a nice one-liner :) 也许有人可以拿出一个漂亮的单行:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM