私はこれまでのところ、これまでに合計を保持するために2つのアキュムレータを使用して、大きなリストの要素の平均を計算するには、この非常に単純な機能を持っており、数:ハスケルでの怠惰と尾の再帰、なぜこのクラッシュですか?
今mean = go 0 0
where
go s l [] = s/fromIntegral l
go s l (x:xs) = go (s+x) (l+1) xs
main = do
putStrLn (show (mean [0..10000000]))
、厳格な言語で、これは希望テール再帰的であり、問題はありません。しかし、ハスケルが怠惰であるため、私のグーグルでは、(s + x)と(l + 1)がサンクとして再帰を受け継いでいることを理解できました。したがって、この全部がクラッシュし、やけど:
Stack space overflow: current size 8388608 bytes.
はさらにグーグルの後、私はseq
と$!
を発見しました。私はこの文脈でそれらを使用しようとする私のすべての試みが無限であることを証明し、無限のタイプについて何かを言っているので、理解できません。
は最後に、私は、再帰呼び出しを変更することにより、それをすべて解決した、-XBangPatterns
が見つかりました:
go !s !l (x:xs) = go (s+x) (l+1) xs
しかし-XBangPatterns
が現在拡張であるように私は、これで満足していません。私は-XBangPatterns
を使わずに評価を厳しくする方法を知りたいです。 (そしておそらく、あまりにも何かを学ぶ!)
あなたが理解の私の欠如を理解するだけので、ここでは(唯一のコンパイル、それがあることをしてみてください)私が試したものです:
go s l (x:xs) = go (seq s (s+x)) (seq l (l+1)) xs
私が理解できるものから、seqはここではsとlの引数の評価を強制する必要があるため、サンクによって引き起こされる問題は回避されます。しかし、私はまだスタックオーバーフローを取得します。
良いもの。 GHCの最適化について知っておいてよかったです。この書籍へのリンクについては に感謝します。素晴らしいリソースのようです。 しかし、私がsthの投稿を見たとき、それは私にそれを打ちました seqの使用がtail-recursionを破るべきであるように見えます。seq は、再帰呼び出しの後に評価する必要があります 評価されたので、尾の再帰の私の理解から、それは はもはや尾を再帰的にする必要はなく、したがってスタックを吹く必要があります。これは のコースは起こらないので、ここで何かが起こっています。 ハスケルはseqを特別に扱いますか?または、私は単に 尾の再帰を混乱させていますか? – Hisnessness
seqは実行時に存在しません。別の評価戦略を使うヒントにすぎません。まったく異なるコードが生成されます。 これは、{ - #STRICT_WHNF# - }プラグマのようなものです。 –