Je cette fonction assez simple pour calculer la moyenne des éléments d'une grande liste, en utilisant deux accumulateurs pour maintenir la somme à ce jour et le nombre jusqu'à présent:La paresse et la récursivité de la queue chez Haskell, pourquoi est ce crash?
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]))
Maintenant, dans une langue stricte, ce serait être récursif à la queue, et il n'y aurait pas de problème. Cependant, comme Haskell est paresseux, mon googling m'a amené à comprendre que (s + x) et (l + 1) seront transmis la récursion comme thunks. Donc, ce ça plante et brûlures:
Stack space overflow: current size 8388608 bytes.
Après plus googler, je trouve seq
et $!
. Ce qu'il semble que je ne comprends pas parce que toutes mes tentatives pour les utiliser dans ce contexte se sont révélées vaines, avec des messages d'erreur disant quelque chose à propos des types infinis.
Enfin, je trouve -XBangPatterns
, qu'il résout tout en changeant l'appel récursif:
go !s !l (x:xs) = go (s+x) (l+1) xs
Mais je ne suis pas content de cela, comme -XBangPatterns
est actuellement une extension. Je voudrais savoir comment rendre l'évaluation stricte sans l'utilisation de -XBangPatterns
. (! Et peut-être apprendre quelque chose de trop)
que vous compreniez bien mon manque de compréhension, voici ce que j'ai essayé (le seul essai que compilé, c'est):
go s l (x:xs) = go (seq s (s+x)) (seq l (l+1)) xs
D'après ce que je pouvais comprendre, seq devrait ici forcer l'évaluation de l'argument s et l, évitant ainsi le problème causé par les thunks. Mais j'ai toujours un débordement de pile.
Bonne affaire. Bon à savoir sur les optimisations GHC, et merci pour le lien vers le livre, ressemble à une excellente ressource. Cependant, quand j'ai regardé le poste de sth, il m'a semblé que pour moi, il semble que l'utilisation de seq devrait casser la queue-récursion.seq doit être évalué après l'appel récursif à aller a été évalué , donc d'après ma compréhension de la récursivité de la queue, il devrait ne plus être récursif, et donc souffler la pile. Ceci de cours ne se passe pas, donc quelque chose se passe ici. Est-ce que Haskell traite séq spécialement? Ou suis-je simplement confus au sujet de queue-récursion? – Hisnessness
seq n'existe pas au moment de l'exécution. C'est juste un indice pour utiliser une stratégie d'évaluation différente. Vous obtiendrez un code entièrement différent généré. Pensez-y plutôt à un pragma {- # STRICT_WHNF # -} –