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)))
Clojreの入門書
Clojreの入門書です。PDFやepub、mobi形式などの本を購入することもできます。 とても丁寧でよいかもです。
4clojure #67 Prime Numbers
4clojure #67 Prime Numbers
最初のx個の素数を返す関数を作る。
(= (__ 2) [2 3]) (= (__ 5) [2 3 5 7 11]) (= (last (__ 100)) 541)
はじめは、reduce
のClojureDocをみて、素数列をつくろうとしましたが、
うまくいかなかったので、prime?
を作ってみました。
1 と自分自身以外に正の約数を持たない自然数で、1 でない数のことが素数
なので、その条件をコード化しました。take
で(dec n)
としているのは2から
始まるiterate
のためです。
あと、prime?
にわたすiterate
のせいで、Execution Timed Out!
がでてしまうので、求める数の二乗の個数までに制限を入れました。
((fn my-prime [num] (letfn [(prime? [n] (if (>= 1 (count (filter #(= 0 (mod n %)) (take (dec n) (iterate inc 2))))) true false))] (take num (filter prime? (take (* num num) (iterate inc 2)))))) 5)
prime?
ができたのであらためてreduce
方式のとき方を見直したらすんなりできました。
((fn my-prime [num] (take num (reduce (fn inner-prime [p n] (if (= 0 (count (filter #(= 0 (mod n %)) p))) (conj p n) p)) [2] (take (* num num) (iterate inc 3))))) 5)
参考 reduceのClojureDocより
(reduce (fn [primes number] (if (some zero? (map (partial mod number) primes)) primes (conj primes number))) [2] (take 1000 (iterate inc 3)))
4clojure #66 Greatest Common Divisor
4clojure #66 Greatest Common Divisor
最大公約数を求める関数を作る。
(= (__ 2 4) 2) (= (__ 10 5) 5) (= (__ 5 7) 1) (= (__ 1023 858) 33)
a
、b
の最大公約数とは、aでもbでも割り切れる数のうちの最大値だから
((fn my-gcd [a b] (let [m (max a b)] (last (filter #(and (= 0 (mod a %)) (= 0 (mod b %))) (take m (iterate inc 1)))))) 1023 858)
4clojure #65 Black Box Testing
4clojure #65 Black Box Testing
シーケンスの種類(:map :set :list :vector)を答える。 ポイントはそれらを調べ、動作を理解することです。
ただし、以下は使用禁止。
class type Class vector? sequential? list? seq? map? set? instance? getClass
(= :map (__ {:a 1, :b 2})) (= :list (__ (range (rand-int 20)))) (= :vector (__ [1 2 3 4 5 6])) (= :set (__ #{10 (rand-int 5)})) (= [:map :set :vector :list] (map __ [{} #{} [] ()]))
動作を調べるといってもよくわからず、ググって答えを探しました。
(fn seq-type [coll] (let [base (empty coll)] (cond (= base {}) :map (= base #{}) :set (reversible? base) :vector (= base ()) :list)))
4clojure #63 Group a Sequence
4clojure #63 Group a Sequence
関数fとシーケンスsを与えてマップを返す関数を作る。 キーはsの各項にfをapplyした値です。 各キーに対応する値はsの順番で連続する項の続くベクターです。
※ group-by
は使用禁止。
(= (__ #(> % 5) [1 3 6 8]) {false [1 3], true [6 8]}) (= (__ #(apply / %) [[1 2] [2 4] [4 6] [3 6]]) {1/2 [[1 2] [2 4] [3 6]], 2/3 [[4 6]]}) (= (__ count [[1] [1 2] [3] [1 2 3] [2 3]]) {1 [[1] [3]], 2 [[1 2] [2 3]], 3 [[1 2 3]]})
まず、map
で各seq
にf
を作用させて、結果と元の値とのリストを作り
ソートして結果ごとに小グループを作る。
((fn my-gr [f coll] (partition-by first (sort-by first (map #(list (f %) %) coll)))) #(> % 5) [1 3 6 8]) ;=> (((false 1) (false 3)) ((true 6) (true 8)))
あとは、求める形式に表現を変更するだけ。
(into {} (map #(hash-map (first (first %)) (into [] (map second %))) '(((false 1) (false 3)) ((true 6) (true 8))))) ; => {false [1 3], true [6 8]}
group-by
の定義には劣るけれども自力でとけたのでよかったです。
((fn my-gr [f coll] (into {} (map #(hash-map (first (first %)) (into [] (map second %))) (partition-by first (sort-by first (map #(list (f %) %) coll)))))) #(> % 5) [1 3 6 8])
参考 group-by
の定義
(defn group-by "Returns a map of the elements of coll keyed by the result of f on each element. The value at each key will be a vector of the corresponding elements, in the order they appeared in coll." {:added "1.2" :static true} [f coll] (persistent! (reduce (fn [ret x] (let [k (f x)] (assoc! ret k (conj (get ret k []) x)))) (transient {}) coll)))
Adam Bardさん特選記事
My Top Clojure Articles
Adam Bardさんが書いたブログ記事のなかから、特選記事をまとめたものです。