Clojure:遍历集合的映射 [英] Clojure: iterate over map of sets
问题描述
这几乎是我最后一个问题的后续工作( Clojure惯用的方式来更新地图的多个值),但不完全一样。 (请记住,我对Clojure和函数式语言都很新)
假设我有以下数据结构,定义为集合映射:
(def m1 {:1#{2}:2#{1 3}:3#{1}})
以及地图的地图:
(def m2 {:1 {:10:2 12:3 23}:2 {:1 23:2 0:3 4}:3 {:1 2:2 4:3 0} }
我想要做的是更新 m2
,它们在m1中与某个值具有对应关系。假设我想要的值是
x
。 m2
会是这样:
{:1 { :10:2 x:3 23}:2 {:1 x:2 0:3 x}:3 {:1 x:2 4:3 0}}
pre>
假设
v
包含我的地图的每一个可能的键,首次尝试,是这样做的:(假设x = 1
(for [iv]
reduce(fn [mj](assoc-in m [ij] 1))d(i m1)))
不用说这是一个失败。
解决方案根据我的理解你的要求
- 从
m1
生成多个键序列。
m2
将每个键序列与特定的常数值相关联。
第一步是对
m1
进行相当简单的转换。我们想为每个条目产生 0个或更多key-seqs (取决于它的集合中有多少个),所以对我来说自然的转换是mapcat
。它代表map
-then -concat
并且是伟大的只是这样的情况下,从每个元素seq我们产生0个或更多我们想要的元素。(defn key-seqs [coll]
(mapcat
(fn [[kv]]
(map(partial vector k)v))
coll))
(key-seqs m1)
;; = ([:1 2] [:2 1] [:2 3] [:3 1])$ b $ b注意,
mapcat
采用的函数本身是一个map
,所以它产生一个(可能为空)mapcat
以展开。但这是返回存储在集合中的渴望本身。如果我们想把它们变成关键字匹配m2
,我们需要一些更多的处理:(defn key-seqs [coll]
(mapcat
(fn [[kv]]
b $ b coll))
(key-seqs m1)
;; => ([:1:2] [:2:1] [:2:3] [:3:1])$ b $ b(我们需要使用
str
,因为关键字
不知道如何处理longs。关键字不是数字,而是具有某种符号含义的名称)
然后我们可以调整
update-m
上一个问题,以便它可以将常量值作为参数,并处理不只是具有相同的值两次的键序列:(defn update-m [mxv]
(reduce(fn [m'key-seq]
(assoc-in m'key-seq x));; accumulate changes
m ;; initial-value
v));;收集到循环
,现在我们似乎在业务:
(update-m m2 1(key-seqs m1))
;; => {:1 {:1 0,:2 1,:3 23},:2 {:11,:2 0,:3 1},:3 {:11,:2 4, b $ b
This is pretty much a follow-up to my last question (Clojure idiomatic way to update multiple values of map), but not quite the same. (keep in mind that I'm fairly new to Clojure and functional languages alike)
suppose I have the following data structure, defined as a map of sets:
(def m1 {:1 #{2} :2 #{1 3} :3 #{1}})
and a map of maps as such:
(def m2 {:1 {:1 0 :2 12 :3 23} :2 {:1 23 :2 0 :3 4} :3 {:1 2 :2 4 :3 0}})
What I want to do is update the registries of
m2
that have a correspondence in m1 to a certain value. Let's say the value I want isx
. The resultingm2
would be something like this:{:1 {:1 0 :2 x :3 23} :2 {:1 x :2 0 :3 x} :3 {:1 x :2 4 :3 0}}
Assuming that
v
contains every possible key for my map, y first attempt, (that I failed miserably) is to do something like this: (assume thatx=1
(for [i v] reduce (fn [m j] (assoc-in m [i j] 1)) d (i m1)))
needless to say that it was a failure. So, how is the idiomatic way to do this?
解决方案As I understand your requirements you want to
- Produce a number of key-sequences from
m1
.- In
m2
associate each of those key-sequences with a particular constant value.The first step is a fairly simple transformation of
m1
. We want to produce 0 or more key-seqs for each entry (depending on how many are in its set), so the natural go-to for me ismapcat
. It stands formap
-then-concat
and is great for just such a case where from each element of a seq we produce 0 or more of the elements we want.(defn key-seqs [coll] (mapcat (fn [[k v]] (map (partial vector k) v)) coll)) (key-seqs m1) ;;=> ([:1 2] [:2 1] [:2 3] [:3 1])
Note that the function taken by
mapcat
is itself amap
, so that it produces a sequence (possibly empty) formapcat
to unroll. But this is returning the longs stored in the sets as themselves. If we want to turn them into keywords to matchm2
we need a little more processing:(defn key-seqs [coll] (mapcat (fn [[k v]] (map (comp (partial vector k) keyword str) v)) coll)) (key-seqs m1) ;;=> ([:1 :2] [:2 :1] [:2 :3] [:3 :1])
(We need to use
str
becausekeyword
doesn't know what to do with longs. Normally keywords are not numbers, but names with some symbolic meaning)Then we can adapt the
update-m
from your previous question a little bit so that it can take the constant value as an argument and handle key-sequences that don't just have the same value twice:(defn update-m [m x v] (reduce (fn [m' key-seq] (assoc-in m' key-seq x)) ;; accumulate changes m ;; initial-value v)) ;; collection to loop over
and now we seem to be in business:
(update-m m2 1 (key-seqs m1)) ;;=> {:1 {:1 0, :2 1, :3 23}, :2 {:1 1, :2 0, :3 1}, :3 {:1 1, :2 4, :3 0}}
这篇关于Clojure:遍历集合的映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!