ehirdの答えは非常によく物事を説明し、しかし一点
があります最終的な結果は、かつての実装が値をキャッシュし、これまでより効率的に後者よりもあるということです。
時々間違っています。あなたは(私は-O2、ない-O1確認、そしてもちろんのみGHC)の最適化と定義のいずれかを含むモジュールをコンパイルする場合
、考慮すべきいくつかのケースがあります:タイプなし
が
- 最初の定義署名
- 型シグネチャと第二定義
fib :: Int -> Integer
- 多型タイプを持つ最初の定義
fib :: Num a => Int -> a
- 型署名することなく、第2の定義
- ケース1内型シグネチャ
fib :: Num a => Int -> a
と2番目の定義は、単相性制限がタイプfib :: Int -> Integer
を生成し、リストmap fib' [0 .. ]
はfib
のすべての呼び出し間で共有されています。つまり、fib (10^6)
を照会すると、メモリに最初の百万(+1)のフィボナッチ数のリストがあり、ガベージコレクターが使用されなくなったと判断できる場合にのみ収集されます。それはしばしばメモリリークです。ケース2では
は、結果は(INGのコア)は、ケース4において
の場合と実質的に同一であり、リストは(もちろんfib
の異なるトップレベル呼出し間で共有されず、結果ができ多くのタイプがあるので、共有するリストがたくさんありますが)、トップレベルの呼び出しごとに1回インスタンス化され、fib'
からの呼び出しで再利用されるため、fib n
を計算すると、O(n)の追加とO(n^2)リスト。それはあまりにも悪くありません。計算が完了するとリストが収集されるため、スペースがリークすることはありません。
ケース3は、4
ケース5と実質的に同じであるが、しかし、素朴な再帰よりも悪いです。明示的に多相でリストはラムダの内側にバインドされているため、リストは再帰呼び出しに再利用できません。それぞれの再帰呼び出しによって新しいリストが作成されます。
この回答とDaniel Fischerの回答は相互に再帰的です。 – misterbee
@misterbee:運良く、ハスケルのプログラマーだけがそれらを読んで、私たちは怠け者でしょうか? – leftaroundabout