Clojure:迭代集合

时间:2015-09-21 05:49:25

标签: clojure lisp idiomatic

这几乎是我上一个问题(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 个答案:

答案 0 :(得分:3)

据我了解您的要求

  1. m1生成许多键序列。
  2. m2中将每个键序列与特定的常量值相关联。
  3. 第一步是对m1进行相当简单的转换。我们希望为每个条目生成 0或更多key-seqs (取决于其集合中的数量),因此对我来说,自然的选择是mapcat。它代表map - 然后 - concat并且非常适用于这样一种情况,即从seq的每个元素中我们生成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不知道如何处理long。通常关键字不是数字,而是具有某些符号含义的名称)

    然后我们可以稍微调整前一个问题中的update-m,以便它可以将常量值作为参数并处理不具有两次相同值的键序列:< / p>

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

答案 1 :(得分: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)

答案 2 :(得分: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}},这基本上是将每个集合转换为映射,其中key是集合的值,值是常量x。
  • 合并m2和新m1。

注意:假设m1中的所有键都以m2为单位。