tengo esta función bastante simple para calcular la media de los elementos de una lista grande, con dos acumuladores para mantener la suma hasta el momento y el recuento hasta el momento:Pereza y recursividad de cola en Haskell, ¿por qué se cuelga?
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]))
Ahora, en un lenguaje estricto, esto ser recursivo de la cola, y no habría problema. Sin embargo, como Haskell es flojo, mi Google me ha llevado a entender que (s + x) y (l + 1) se transmitirán por la recursión como thunks. Así que todo esto esta cosa accidentes y quemaduras:
Stack space overflow: current size 8388608 bytes.
Tras Google aún más, he encontrado seq
y $!
. Lo cual parece que no entiendo porque todos mis intentos de usarlos en este contexto resultaron inútiles, con mensajes de error que dicen algo acerca de los tipos infinitos.
Finalmente encontré -XBangPatterns
, que resuelve todo cambiando la llamada recursiva:
go !s !l (x:xs) = go (s+x) (l+1) xs
, pero no estoy feliz con esto, como -XBangPatterns
es actualmente una extensión. Me gustaría saber cómo hacer que la evaluación sea estricta sin el uso de -XBangPatterns
. (! Y tal vez aprender algo demasiado)
Para que lo entienda mi falta de entendimiento, esto es lo que he intentado (el único try que compilado, se entiende):
go s l (x:xs) = go (seq s (s+x)) (seq l (l+1)) xs
Por lo que pude entender, SEC debería forzar la evaluación del argumento s y l, evitando así el problema causado por los thunks. Pero todavía tengo un desbordamiento de pila.
Cosas buenas. Es bueno saber sobre las optimizaciones de GHC, y gracias por el enlace al libro, parece un gran recurso. Sin embargo, cuando miré la publicación de sth, me pareció que parece que el uso de seq debería interrumpir la recursión de la cola.seq debe evaluarse después de que la llamada recursiva para ir haya sido evaluada, por lo que a partir de mi comprensión de recursividad de cola, debería ya no ser recursivo de cola, y así volar la pila. Esto del curso no ocurre, entonces algo está pasando aquí. ¿ Haskell trata seq especialmente? ¿O simplemente estoy confundido acerca de cola-recursividad? – Hisnessness
seq no existe en tiempo de ejecución. Es solo una pista para usar una estrategia de evaluación diferente. Obtendrá un código completamente diferente generado. Piénselo más como un {- # STRICT_WHNF # -} pragma –