4clojure #69 Merge with a Function
4clojure #69 Merge with a Function
関数f
といくつかのマップを引数に取る関数を作る。
その関数は、一番目のマップにconj
したマップの残りから構成されたマップを返す。
複数のマップにひとつのキーがあるとき、後ろからのマッピングは(f 結果の値 後の値)
を
読んだ結果にマッピングされる。
※ merge-with
は使用禁止。
(= (__ * {:a 2, :b 3, :c 4} {:a 2} {:b 2} {:c 5}) {:a 4, :b 6, :c 20}) (= (__ - {1 10, 2 20} {1 3, 2 10, 3 15}) {1 7, 2 10, 3 15}) (= (__ concat {:a [3], :b [6]} {:a [4 5], :c [8 9]} {:b [7]}) {:a [3 4 5], :b [6 7], :c [8 9]})
maps
を2つに分割します。
((fn my-merge [func & maps] (reduce #(println %1 %2) (first maps) (rest maps))) * {:a 2, :b 3, :c 4} {:a 2} {:b 2} {:c 5}) ; {:a 2, :c 4, :b 3} {:a 2} ; nil {:b 2} ; nil {:c 5} ; nil
rest map
にreduce
を作用させて欲しいmap
を作ります。
((fn my-merge [func & maps] (reduce (fn [a b];(println a (reduce (fn [x y] (println (key y) (a (key y)) (val y) (if (nil? (a (key y))) (val y) (func (a (key y)) (val y))))) '{} b)) (first maps) (rest maps))) - {1 10, 2 20} {1 3, 2 10, 3 15}) ; * {:a 2, :b 3, :c 4} {:a 2} {:b 2} {:c 5}) ; 1 10 3 7 ; 2 20 10 10 ; 3 nil 15 15 ; nil
rest map
が複数になることがあるので、2段目のreduce
の引数はa b
ではなく
b a
が正解。
かなり手こずりましたが何とか解決しました。
((fn my-merge [func & maps] (reduce (fn [a b] ;(println a b) (reduce (fn [x [k v]] ;(println x k v) (assoc x k (if (nil? (b k)) v (func v (b k)))) ) b a)) (first maps) (rest maps))) * {:a 2, :b 3, :c 4} {:a 2} {:b 2} {:c 5}) ; - {1 10, 2 20} {1 3, 2 10, 3 15})
参考
;; hyone's solution to Merge with a Function ;; https://4clojure.com/problem/69 (fn my-merge-with [f & maps] (reduce (fn [a b] (reduce (fn [x [k v]] (assoc x k (if (b k) (f v (b k)) v))) b a)) (first maps) (rest maps)))
参考 merge-with
の定義
(defn merge-with "Returns a map that consists of the rest of the maps conj-ed onto the first. If a key occurs in more than one map, the mapping(s) from the latter (left-to-right) will be combined with the mapping in the result by calling (f val-in-result val-in-latter)." {:added "1.0" :static true} [f & maps] (when (some identity maps) (let [merge-entry (fn [m e] (let [k (key e) v (val e)] (if (contains? m k) (assoc m k (f (get m k) v)) (assoc m k v)))) merge2 (fn [m1 m2] (reduce1 merge-entry (or m1 {}) (seq m2)))] (reduce1 merge2 maps)))) ;; reduce is defined again later after InternalReduce loads (defn ^:private ^:static reduce1 ([f coll] (let [s (seq coll)] (if s (reduce1 f (first s) (next s)) (f)))) ([f val coll] (let [s (seq coll)] (if s (if (chunked-seq? s) (recur f (.reduce (chunk-first s) f val) (chunk-next s)) (recur f (f val (first s)) (next s))) val))))
参考 merge
の定義
(defn merge "Returns a map that consists of the rest of the maps conj-ed onto the first. If a key occurs in more than one map, the mapping from the latter (left-to-right) will be the mapping in the result." {:added "1.0" :static true} [& maps] (when (some identity maps) (reduce1 #(conj (or %1 {}) %2) maps)))