在Clojure中的某个范围内的地图列表中填写缺失值

时间:2016-06-22 15:31:58

标签: clojure

如果我们的意见如下:

 (({:a 1 :b 100} {:a 2 :b 300} {:a 4 :b 0}) ({:a 0 :b 10} {:a 4 :b 50}))

我们想要监管的范围是(0 1 2 3 4)      我们希望输出为:

 (({:a 0 :b 0} {:a 1 :b 100} {:a 2 :b 300} {:a 3 :b 0} {:a 4 :b 0}) 
 ({:a 0 :b 10} {:a 1 :b 0} {:a 2 :b 0} {:a 3 :b 0} {:a 4 :b 50}))

基本上它应该做的是查看第一个地图列表然后看第二个等等,然后找出:a的范围。我们可以使用min / max函数轻松完成。现在它创建一个范围并将其应用于两个列表。如果某个列表中缺少:a,则会在:a中添加:b 0。 (即在第一个列表中添加{:a 0 :b 0}{:a 3 :b 0}。我们有一个功能可以在某种程度上做到,但还没有完成。这就是:

 (map 
    #(doseq [i (vec myRange)] 
        (if (some (fn [list] (= i list)) (map :a %)) 
            nil 
            (println (conj % {:a i :b 0}))))
     myList)

显然,由于Clojures不可变数据结构,此函数失败。如果我们的输入是这样的:

 (({:a 1, :b 1} {:a 2, :b 3} {:a 4, :b 5}) 
  ({:a 0, :b 3} {:a 4, :b 1}))

我们的输出是:

 (nil nil)

但如果我们打印:

 ({:a 0, :b 0} {:a 1, :b 1} {:a 2, :b 3} {:a 4, :b 5})
 ({:a 3, :b 0} {:a 1, :b 1} {:a 2, :b 3} {:a 4, :b 5})
 ({:a 1, :b 0} {:a 0, :b 3} {:a 4, :b 1})
 ({:a 2, :b 0} {:a 0, :b 3} {:a 4, :b 1})
 ({:a 3, :b 0} {:a 0, :b 3} {:a 4, :b 1})
 (nil nil) 

我们希望输出看起来像:

(({:a 0, :b 0} {:a 1, :b 1} {:a 2, :b 3} {:a 3, :b 0} {:a 4, :b 5})
 ({:a 0, :b 3} {:a 1, :b 0} {:a 2, :b 0} {:a 3, :b 0} {:a 4, :b 1}))

不使用println。有什么建议吗?

2 个答案:

答案 0 :(得分:3)

在循环中使用不可变数据的想法是将最新迭代的结果传递给下一个迭代。您可以使用loop/recur执行此操作,但在您的情况下,通常使用reduce函数(这实际上是函数式编程的基础之一):

(defn update-coll [range items]
  (reduce (fn [items i] (if (some #(= (:a %) i) items)
                          items 
                          (conj items {:a i :b 0})))
          items range))

reduce的第一个参数“为范围(items)的每个值更新”i,将更新的值传递给下一次迭代。

现在您只需要用它来映射您的输入数据:

(def input '(({:a 1 :b 100} {:a 2 :b 300} {:a 4 :b 0})
             ({:a 0 :b 10} {:a 4 :b 50})))

(map (comp (partial sort-by :a) 
           (partial update-coll [0 1 2 3 4])) 
     input)

输出:

(({:a 0, :b 0} {:a 1, :b 100} {:a 2, :b 300} 
  {:a 3, :b 0} {:a 4, :b 0}) 
 ({:a 0, :b 10} {:a 1, :b 0} {:a 2, :b 0} 
  {:a 3, :b 0} {:a 4, :b 50})) 

你也可以不使用clojure的集合来实现它:

(defn process-input [input r]
  (let [r (map #(hash-map :a % :b 0) r)]
    (map (fn [items] (into (apply sorted-set-by
                                  #(compare (:a %1) (:a %2))
                                  items)
                           r))
         input)))

(process-input input [0 1 2 3 4])

输出:

(#{{:b 0, :a 0} {:a 1, :b 100} {:a 2, :b 300} 
   {:b 0, :a 3} {:a 4, :b 0}} 
 #{{:a 0, :b 10} {:b 0, :a 1} {:b 0, :a 2} 
   {:b 0, :a 3} {:a 4, :b 50}})

答案 1 :(得分:1)

我的尝试:

(defn fill-in-missing [lists]
  (let [[min max] (apply (juxt min max) (map :a (flatten lists)))]
    (for [cur-list lists]
      (for [i (range min (inc max))]
        (merge {:a i :b 0} 
               (some #(when (= i (:a %)) %) cur-list))))))

要获得:a的最小值和最大值,我只需使用:aflatten收集每个map,然后使用juxt,我就可以{ {1}} applymin同时对他们起作用 由于我们需要两个级别的嵌套列表,因此我选择了两个for列表推导,并尝试创建一个尝试在输入中查找地图的表达式,或者返回默认的max