怠惰と末尾再帰ウ、なぜこのチ?
-
06-07-2019 - |
質問
私はこのシンプルなものにな機能を計算するための平均の大きなリストを用いたアキュムレータの和を、これまでに数では:
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]))
現在、厳しい言語になり得るかということですが、テール-再帰的であれば問題ありません。しかし、ウものぐさの私のgooglingにつながっていると理解(s+x)及び(l+1)上げる必要があり、繰り返してthunks.ここの衝突の際の傷:
Stack space overflow: current size 8388608 bytes.
その後、googlingん 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
だからこそ理解でき、配列番号はこちら力の評価であり、s、lの引数を避けることが出来る問題によるthunks.だスタックオーバーフロー.
解決
かわこ
まず、ありたい場合には厳しい評価のアキュムレータの使用 seq
滞ウ98:
mean = go 0 0
where
go s l [] = s / fromIntegral l
go s l (x:xs) = s `seq` l `seq`
go (s+x) (l+1) xs
main = print $ mean [0..10000000]
*Main> main
5000000.0
第二に、厳密に分析を始める場合に伝えるための型の注釈、コンパイル-O2:
mean :: [Double] -> Double
mean = go 0 0
where
go :: Double -> Int -> [Double] -> Double
go s l [] = s / fromIntegral l
go s l (x:xs) = go (s+x) (l+1) xs
main = print $ mean [0..10000000]
$ ghc -O2 --make A.hs
[1 of 1] Compiling Main ( A.hs, A.o )
Linking A ...
$ time ./A
5000000.0
./A 0.46s user 0.01s system 99% cpu 0.470 total
で"ダブル"はラッパーの厳しい原子タイプダブルル#、最適化、精密タイプ、GHC運厳密に分析、推論の厳しいバージョンです。
import Data.Array.Vector
main = print (mean (enumFromToFracU 1 10000000))
data Pair = Pair !Int !Double
mean :: UArr Double -> Double
mean xs = s / fromIntegral n
where
Pair n s = foldlU k (Pair 0 0) xs
k (Pair n s) x = Pair (n+1) (s+x)
$ ghc -O2 --make A.hs -funbox-strict-fields
[1 of 1] Compiling Main ( A.hs, A.o )
Linking A ...
$ time ./A
5000000.5
./A 0.03s user 0.00s system 96% cpu 0.038 total
に記載のとおりRWH章ます。
他のヒント
の seq
能力評価の最初のパラメータの関数が呼び出されます。き seq s (s+x)
パラメータとしての seq
機能 ない という、すぐに必要としないの価値を評価するのがパラメータとします。また、呼び出 seq
を評価することができる前に再帰呼出し、そのことにつながるよう期待しています力は、そのパラメータを評価することができます。
通常、これはこのリンク:
go s l (x:xs) = s `seq` l `seq` go (s+x) (l+1) xs
この統語変化 seq s (seq l (go (s+x) (l+1) xs))
.こちらの電話 seq
の一番外側の機能に異なるアイコンで表示されます。がウの怠惰これによって評価され: seq
をとるものunevaluatedパラメータ s
や seq l (go (s+x) (l+1) xs)
, 評価パラメータは繰り延べ、誰かが実際にアクセスしようとしその値には使用できません。
現在 seq
能力の最初のパラメータに対する評価を返す前に他の異なるアイコンで表示されます。そして、次の段階の評価の第二 seq
.の場合電話 seq
埋蔵されているかの一部のパラメータではない実行するために長時間を倒その目的性にある。
の変更の seq
sのプログラムを実行し、を使用せずに過大な量のメモリを消費します。
別の解決の問題をうける最適化にGHCがプログラムの作成(-O
または -O2
).バージョンのオプティマイザが認識さを考慮する必要怠惰とコードしない配置が不要。
すでにご理解する seq s (s+x)
力の評価 s
.な力 s+x
, こだビthunks.
を使用 $!
できる力の評価のほか、二回、両方の引数).この同じ効果としてのバーンパターン:
mean = go 0 0
where
go s l [] = s / fromIntegral l
go s l (x:xs) = ((go $! s+x) $! l+1) xs
の利用 $!
機能する go $! (s+x)
に相当する:
let y = s+x
in seq y (go y)
このように y
最初に強 弱ヘッド標準形, ここで、一番外側の機能が適用されます。の場合 y
, は、一番外側の機能を +
, ること y
完全評価に渡される前に go
.
ああ、と思った無限のタイプのエラーメッセージできなかったの括弧に位置しています。また、同じエラーが書いたプログラムの下:-)
なので $!
オペレーターが連想なく、括弧 go $! (s+x) $! (l+1)
と同: go $! ((s+x) $! (l+1))
, ことが明らかである間違っています。