Arantium Maestum

プログラミング、囲碁、読書の話題

lazy-seqでrepeat, repeatedly, cycleを書いてみる

Clojureの勉強に4clojureというサイトを使っている。

プログラミングで比較的ポピュラーな公案形式の問題集である。

よく出てくる問題の形式として、Clojureの基本関数を他の基本関数を使って実装してみる、というものがある。いい機会なのでlazy-seqという遅延評価のシーケンスを作成する関数を使って、無限かもしれないシーケンスを作成する関数群を実装してみる。

repeat

(defn my-repeat 
  ([x] (lazy-seq (cons x (my-repeat x))))
  ([n x] (take n (my-repeat x))))

とりあえず引数が1つだけの場合は無限にその引数を返し続けるシーケンス。引数が[n x]と二つある場合は、xをn回返すシーケンス。末尾再帰でもなさそうなので(loop/recurを使っていないから)stack overflowの可能性があるかと思って

(apply + (take 100000000 (my-repeat 5)))

こんなコードを試してみたが、時間はかかっても問題なく実行できた。lazy-seqでも末尾再帰になってるのだろうか。

あとmulti-arity対応が感動的に楽。

repeatedly

(defn my-repeatedly
  ([f] (lazy-seq (cons (f) (my-repeatedly f))))
  ([n f] (take n (my-repeatedly f))))

ほぼrepeatと同じ理屈。同じ関数を何回も呼び出す、ということで乱数や副作用のある関数が対象になる。(純粋関数だったら一回呼び出して結果をrepeatするだけでもいいので)

cycle

(defn my-cycle [col]
  (lazy-seq (concat col (my-cycle col))))

arityが一つに定まっている分シンプル。repeatrepeatedlyが一つの値や関数の戻り値の連なりだったのに対して、コレクションの内容のループなのでconsではなくconcatを使っている。concat自身もlazyなので問題なく使える。末尾再帰も行われているようだ。

次はrange, map, filteriteratelazy-seqで書いてみたい。