fixと無限リストの相性
昨日のポストは単なるいかさまだったわけですが、これに懲りずに、また何か展開があったら懲りずにデータ再帰についてポストしていきたいと思います。(泣)
それとは別に、fixで無限リストを生成するコードが書けたのでそれについて簡単に書いておきたいと思います。
a -> aな関数fをパラメタにとるときにfixで値を返す方法の一つはこんなやつでした:
fix (\_ -> 1) => 1
fixで無限再帰がいつ起きるかというと関数fがそのパラメタを評価したときです。だから、パラメタを見る前に値を返さなくてはいけないわけですね…更に、fixの特徴として、fix fが返した値はfへのパラメタがとしてそのままわたってくるわけです。こんなのはどうでしょう?
fix (\x -> 1 : x)
これはrepeat 1と同じ結果で無限個の1を含むリストができます…無限リストを返す関数ならば、無限再帰になっても使い道はありますね…
take 5 $ fix (\x -> 1 : x) => [1, 1, 1, 1, 1]
となるわけです。
ちょっとひねって:
take 5 $ fix (\x -> 1 : map (+1) x) => [1, 2, 3, 4, 5]
なんていうのもできます…
フィボナッチ数列をやってみました。i番目の値は:
fib i = if i > 1 then fib(i - 1) + fib(i - 2) else i
という数式で求められます。つまり、自分の直前2つの要素の和なわけですね…(最初の2つは1ですね。)
こうゆう再帰はf:: (a->a)->a->aなfixで書けば:
fib = fix (\f i -> if i <= 2 then 1 else (f $ i - 1) + (f $ i - 2))
とかけます。これをf::a->aなfixでやるのはどうも無理そうです。何せfにfixの外側からパラメタiを与えて、それを再帰が深くなるごとにi-1とやったりする方法がないからです。でも、最初にあげた無限リストを使った方法ならこうかけます:
fib = fix $ \x -> [1, 1] ++ (zipWith (+) x $ tail x) (print.(take10)) fib => [1,1,2,3,5,8,13,21,34,55]
みたいな感じですね…
なんとなくfixの使い方がわかってきた気がします。
ただ、できなかったのはf::a->aなfixで階乗を書くことです…
今日はこの辺で。
ではでは。