Skip to content

Instantly share code, notes, and snippets.

@plaster

plaster/pe-2.clj Secret

Created December 10, 2012 14:40
Show Gist options
  • Save plaster/d34487ab2ba5a3f375f3 to your computer and use it in GitHub Desktop.
Save plaster/d34487ab2ba5a3f375f3 to your computer and use it in GitHub Desktop.
(defn internal-fold-with-fib-to
[op seed end x0 x1]
(if (< x1 end)
(recur op (op x1 seed)
end
x1
(+ x0 x1))
seed
))
(defn fold-with-fib-to
[op seed end]
(internal-fold-with-fib-to op seed end 0 1)
)
;; 当初 fold-with-fib-to と internal-fold-with-fib-to は
;; 一つの関数で、引数の数でどっちに行くかを分けてみていたのですが
;; recur がうまくいかなかったので、あきらめて2つに分けました
(defn solve []
(fold-with-fib-to
(fn [x sum]
(if (even? x)
(+ x sum)
sum))
0
4000001
))
(defn fold-with-fib-to [op seed end]
(loop [seed seed
x0 0
x1 1]
(if (< x1 end)
(recur (op x1 seed)
x1
(+ x0 x1))
seed
)))
(defn solve [end-inclusive]
(fold-with-fib-to
(fn [x sum]
(if (even? x)
(+ x sum)
sum))
0
(+ 1 end-inclusive)
))
@kohyama
Copy link

kohyama commented Dec 10, 2012

畳み込み渋いですね. 惚れます.

関数名とコメントから推察するに,
internal-... には, 名前を与える必要も, fold-... の外に出す必要も無いと考えていらっしゃると思います.

recur だと, 関数オーバーロードを跨げないので, 少なくとも二つの関数を作る必要はあります.
しかし, 名前を与える必要はありません. つまり匿名関数 fn を使えば良いです.
recurfn に再帰します.
またこの関数を外から呼び出すのは一回ですので, ((fn [仮引数 ...] 本体) 実引数 ...) のように, 作ったその場で使えば良いです.

(defn fold-with-fib-to [op seed end]
  ((fn [i-op i-seed i-end x0 x1]
     (if (< x1 i-end)
         (recur i-op (i-op x1 i-seed) i-end x1 (+ x0 x1))
         i-seed))
     op seed end 0 1))

内部の fn の仮引数で同じ変数名 op, seed, end を使っても何の問題もありませんが,
どっちのスコープにあるか分かり易いように敢えて i- を付けました.

終了条件の与えられる畳み込みが有用な場面は多いので,
あまり成功しているとは言いがたいのですが, 一般化の試み https://gist.github.com/2896939
などしているのでよろしければご参考まで...

@plaster
Copy link
Author

plaster commented Dec 11, 2012

有用なイディオムありがとうございます。 ((fn のところにどうも見覚えがあったのですが、distinctの実装でした。ごく自然に無名関数の再帰ができて便利ですね。

今回は「外から呼び出すのは一回」なのに加えてrecurすべき関数が自明だったので、loopになおしてみました。( #file_pe_2_2.clj )
named-let大好きなScheme脳なので、loop構文つかえると見るや、つい使ってしまいます。

リンク先のr-reduce、まだあまり読めてないのですが(特にretnの役割)、例にあるいろんなシーケンス処理関数の基礎に使えるのかっこいいですね。

@tnoda
Copy link

tnoda commented Dec 11, 2012

その再帰もありましたか > @plaster @kohyama

このパターンも想定していませんでした.今回,本当にいろいろと勉強になります.「末尾再帰 = ループ」と考えれば loop-recur でも書けますね:

(loop [x 0 y 1 sum 0]
  (if (< x 4000000)
    (recur y (+ x y) (if (even? x) (+ sum x) sum))
    sum))

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