#clojure 入門者向け勉強会 #mitori_cljでClojureのメモ化関数についてこんなのどう?という提案をいただいたのでちょっと確認してみました。
結果はやっぱりだめでした。
(12/20 16:05)というのはうそでした。 指摘いただいたやりかたの実装が間違ってました。ひぇー。
以下は修正版です。
(defn fib [n]
(do
(println "-->" n)
(if (<= n 1)
n
(+ (fib (dec n)) (fib (- n 2))))))
これを実行するとこう
user> (fib 4) --> 4 --> 3 --> 2 --> 1 --> 0 --> 1 --> 2 --> 1 --> 0 3 user> (fib 5) --> 5 --> 4 --> 3 --> 2 --> 1 --> 0 --> 1 --> 2 --> 1 --> 0 --> 3 --> 2 --> 1 --> 0 --> 1 5
関数が呼ばれると毎回計算します。
まずは普通にメモ化してみます。
(def fib-outmem (memoize fib))
実行するとこう
user> (fib-outmem 4) --> 4 --> 3 --> 2 --> 1 --> 0 --> 1 --> 2 --> 1 --> 0 3 user> (fib-outmem 5) --> 5 --> 4 --> 3 --> 2 --> 1 --> 0 --> 1 --> 2 --> 1 --> 0 --> 3 --> 2 --> 1 --> 0 --> 1 5
- 関数内部での呼び出しでメモ化が効いてません。
- 外部でも(fib 5)の呼び出しで(fib 4)の結果が使われていません。
提案いただいたのは、memoizeに無名関数を渡してやればいいのでは?というものなのでやってみます。
(def fib-inmem
(memoize
(fn [n]
(do
(println "-->" n)
(if (<= n 1)
n
(+ (fib-inmem (dec n)) (fib-inmem (- n 2))))))))
実行してみると、ちゃんと動きます。
user> (fib-inmem 4) --> 4 --> 3 --> 2 --> 1 --> 0 3 user> (fib-inmem 5) --> 5 5
ちなみに、僕の作ったマクロでメモ化でやってもこうなります。(まちがえて5でやってしまった)
user> (fib-defmemo 5) --> 5 --> 4 --> 3 --> 2 --> 1 --> 0 5 user> (fib-defmemo 6) --> 6 8
fib-inmem
の中の再帰で typo でfib
(メモ化されていない fib) を呼んでいるのが原因と思われます.一応,
memoize
は使えるみたいです. ということで.ただ, 結局
(def foo (memoize foo))
のように先に定義した関数を後からメモ化することができず,(def foo (memoize (fn [] ...)))
のように最初からメモ化関数として書くことしかできませんから,やはり, 御
defn-memo
マクロの方が書きやすくて 👍 です.