Clojure:遍历set集合的map

3
这基本上是我上一个问题(Clojure idiomatic way to update multiple values of map)的跟进,但并不完全相同。(请记住,我对Clojure和函数式语言都还很陌生。)
假设我有以下数据结构,定义为映射集合的地图:
(def m1 {:1 #{2} :2 #{1 3} :3 #{1}})

并且有一个如下的映射表:

(def m2 {:1 {:1 0 :2 12 :3 23} :2 {:1 23 :2 0 :3 4} :3 {:1 2 :2 4 :3 0}})

我想要做的是将在m1中对应的m2注册表更新为特定值。假设我想要的值是x。更新后的m2应该如下所示:
{:1 {:1 0 :2 x :3 23} :2 {:1 x :2 0 :3 x} :3 {:1 x :2 4 :3 0}}

假设v 包含我的地图的所有可能键,我的第一次尝试是像这样做的(我惨败了):(假设x=1)。
(for [i v]
 reduce (fn [m j] (assoc-in m [i j] 1)) d (i m1)))

毋庸置疑,这是一个失败的尝试。那么,什么是惯用的做法呢?
3个回答

3
根据您的要求,我理解您想要:
  1. m1 生成若干个关键序列。
  2. m2 中将这些关键序列与特定常量值相关联。
第一步是对 m1 进行相当简单的转换。我们希望为每个条目(取决于其集合中有多少个条目)生成 0 或多个关键序列,因此我自然而然地想到使用 mapcat。它代表了 map 然后 concat,非常适用于这种情况,在这种情况下,我们从序列的每个元素中产生我们想要的 0 或多个元素。
(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])

请注意,mapcat使用的函数本身是一个map,因此它会生成一个序列(可能为空),以供mapcat展开。但这将返回存储在集合中的长整型数值本身。如果我们想将它们转换为关键字以匹配m2,我们需要进行一些额外的处理:
(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])

我们需要使用str,因为keyword不知道如何处理长整数。通常关键字不是数字,而是具有一定象征意义的名称。

然后,我们可以稍微修改您之前问题中的update-m函数,使其可以将常量值作为参数,并处理不仅两次具有相同值的键序列:

(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

现在我们似乎正在做生意:

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

如果OP一开始就使用数字作为键,而不是问题中的这些伪数字关键字,那么会容易得多。 - amalloy
先生/女士,您真是太棒了。只有一个问题:您能否进一步解释一下这行代码的作用:(map (comp (partial vector k) keyword str) v))我的意思是,这里的comp到底是在做什么?我知道它正在组合部分函数,并将str转换为关键字,但您能否再解释得更详细一些呢? - Arthur Camara
@amalloy 如果我这样做了,会更容易吗?我的意思是,这种情况是否有直接的解决方案? - Arthur Camara
基本过程是mapcat迭代条目[k v],从[:1 #{2}]开始。通过comppartial生成一个函数,如(fn [num] (vector :1 (keyword (str num)))),然后将其映射到集合v中的所有*数字上。这样就得到了一个向量序列,其中所有第一个成员都是:1,然后将其交回给mapcat。然后将所有这些序列组合成一个序列。需要一点时间才能学会阅读这些comppartial组合,因为在返回的函数中,数据首先从最后一个参数fn流向左侧。 - Magos

2

我认为一个好的解决方案是,如果你将m1的数据结构改为类似于以下的形式:

(def m1-new [[:1 :2] [:2 :1] [:2 :3] [:3 :1]])

然后您可以对其进行reduce并使用assoc-in
(reduce (fn [m path] (assoc-in m path my-new-value)) m2 m1-new)

1
尝试这个(这里x为100)
(merge-with merge m2 
   (into {} (for [[k v] m1] [k (into {} (for [i v] [(keyword (str i)) 100]))])))

编辑:

这个想法是:

  • 将m1从{:1 #{2} :2 #{1 3} :3 #{1}}转换为{:1 {:2 x} :2 {:1 x :3 x} :3 {:1 x}},基本上是将每个集合转换为一个映射,其中键是集合的值,而值是常量x。
  • 合并m2和新的m1。

注意:假设m1中的所有键都在m2中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接