これまでの合計とこれまでのカウントを保持するために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]))
さて、厳密な言語では、これは末尾再帰であり、問題はありません。しかし、Haskellは怠惰なので、私のグーグルは、(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の引数の評価を強制し、サンクによって引き起こされる問題を回避する必要があります。しかし、それでもスタックオーバーフローが発生します。