Skip to content

Instantly share code, notes, and snippets.

@kohyama
Last active December 24, 2015 11:09
Show Gist options
  • Save kohyama/6789280 to your computer and use it in GitHub Desktop.
Save kohyama/6789280 to your computer and use it in GitHub Desktop.
How to generalize these three functions.
(ns foo
(:require [clojure.test :refer (with-test is are run-tests)]))
(with-test
(defn flatten-map [m kf vf]
(into {}
((fn g [kv n]
(if (map? n)
(apply concat
(keep (fn [[k v]] (g (conj kv k) v)) n))
(if-let [kfkv (kf kv)]
(if-let [vfn (vf n)]
[[kfkv vfn]]))))
[] m)))
(are [m kf vf _ r] (= (flatten-map m kf vf) r)
{:a {:b {:c "3" :d "4"} :e "5"}} identity identity
-> {[:a :b :c] "3" [:a :b :d] "4" [:a :e] "5"}
{:a {:b {:c "3" :d "4"} :e "5"}}
#(keyword (apply str (interpose ":" (map name %))))
read-string
-> {:a:b:c 3 :a:b:d 4 :a:e 5}))
(with-test
(defn deep-merge [a b]
(reduce (fn [a [kv n]] (assoc-in a kv n))
a
((fn g [kv n]
(if (map? n)
(apply concat
(map (fn [[k v]] (g (conj kv k) v)) n))
[[kv n]]))
[] b)))
(are [a b _ r] (= (deep-merge a b) r)
{:a {:b {:c 3 :d 4} :e 5}}
{:a {:b {:c 6 :f 7}} :g 8}
->
{:a {:b {:c 6 :d 4 :f 7} :e 5} :g 8}))
(with-test
(defn mapf [m f & args]
(reduce (fn [a [kv n]] (assoc-in a kv n))
{}
((fn g [kv n]
(if (map? n)
(apply concat
(keep (fn [[k v]] (g (conj kv k) v)) n))
(if-let [fna (apply f n args)]
[[kv fna]])))
[] m)))
(is (= (mapf {:a {:b 3 :c 4} :d 5} #(* % %))
{:a {:b 9 :c 16} :d 25}))
(is (= (mapf {:a {:b 3 :c 4} :d 5} #(+ (* %1 %1) %2) 1)
{:a {:b 10 :c 17} :d 26}))
(is (= (mapf {:a {:b 3 :c 4} :d 0} #(try (/ %2 %1) (catch Exception e)) 1)
{:a {:b 1/3 :c 1/4}})))
@kohyama
Copy link
Author

kohyama commented Oct 3, 2013

掲載した三つの関数の中には同じ構造をした部分があります.
この共通構造を一つの関数またはマクロ(既存のものでも新たに作ったものでも)で一般化し, それを使ってそれぞれのコードを書き直すことはできるでしょうか.

mapf

(defn mapf [m f & args]
  ((fn g [n]
   (if (map? n)
       (into {} (keep (fn [[k v]] (if-let [gv (g v)] [k gv])) n))
       (apply f n args)))
   m))

の方が速く動作しますが, 他の二つの関数との共通構造を見やすくしたコードを掲載しました.


These three functions have a same structure.
Can we rewrite these functions with using a function or a macro (existing or made) which generalize the structure.

(defn mapf [m f & args]
  ((fn g [n]
   (if (map? n)
       (into {} (keep (fn [[k v]] (if-let [gv (g v)] [k gv])) n))
       (apply f n args)))
   m))

is an implementation of mapf, which works more fast.
But the code is as it is so that we can see the same structure as other two.

@kohyama
Copy link
Author

kohyama commented Oct 3, 2013

mapf は, ねこはる師匠の mapf を参考に

(require '[clojure.walk :as w])
(defn mapf [m f & args]
  (w/walk (fn [[k v]]
            [k (if (map? v)
                   (apply mapf v f args)
                   (apply f v args))])
          identity
          m))

のように書けました.
他の二つも walk で書き直せるでしょうか.
そしてそれらから共通部分を取り出せるでしょうか.
それとも共通部分を取り出す必要が無いくらいそれぞれが簡潔に書けるでしょうか.


Referring Mr. Halcat's mapf, I could rewrite mapf as

(require '[clojure.walk :as w])
(defn mapf [m f & args]
  (w/walk (fn [[k v]]
            [k (if (map? v)
                   (apply mapf v f args)
                   (apply f v args))])
          identity
          m))

.
Can I rewrite other two with walk?
And then, can I retrieve and define an abstraction of the same structure of them?
Or can I make them so simple that I don't want to generalize them?

@kohyama
Copy link
Author

kohyama commented Oct 3, 2013

I got a good reply on Group.
I'll check later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment