tutorial online funciones empresa descargar constructora company haskell

haskell - online - withFile vs. openFile



haskell pdf (5)

Como otros han notado, hGetContents es flojo. Sin embargo, puede agregar rigor si lo desea:

import Control.DeepSeq forceM :: (NFData a, Monad m) => m a -> m a forceM m = do val <- m return $!! val main = do xs <- withFile "text.txt" ReadMode (forceM . getlines) ...

Aunque, en general, se recomienda que realice todos los IO relacionados con el contenido del archivo dentro del bloque withFile . De esta forma, su programa puede aprovechar la lectura de archivos vagos, manteniendo solo la cantidad necesaria en la memoria. Si se trata de un archivo muy grande, forzar que todo el archivo se lea en la memoria suele ser una mala idea.

Si necesita un control más detallado de los recursos, entonces debe considerar el uso de ResourceT (que viene con el paquete de conduit ) o similar.

[edit: usa $!! de Control.DeepSeq (en lugar de $! ) para asegurarse de que todo el valor está forzado. Gracias por el consejo, @benmachine]

Este programa produce el resultado que espero cuando se le da un archivo de entrada de texto delimitado por / n:

import System.IO main :: IO () main = do h <- openFile "test.txt" ReadMode xs <- getlines h sequence_ $ map putStrLn xs getlines :: Handle -> IO [String] getlines h = hGetContents h >>= return . lines

Sustituyendo withFile por openFile y reorganizando ligeramente

import System.IO main :: IO () main = do xs <- withFile "test.txt" ReadMode getlines sequence_ $ map putStrLn xs getlines :: Handle -> IO [String] getlines h = hGetContents h >>= return . lines

Me las arreglé para no obtener ninguna salida en absoluto. Estoy perplejo.

Edición: Ya no perplejo: gracias a todos y todas por las respuestas reflexivas y estimulantes. Hice un poco más de lectura en la documentación y aprendí que withFile se puede entender como una aplicación parcial de paréntesis .

Esto es con lo que terminé:

import System.IO main :: IO () main = withFile "test.txt" ReadMode $ /h -> getlines h >>= mapM_ putStrLn getlines :: Handle -> IO [String] getlines h = lines `fmap` hGetContents h


El archivo se cierra demasiado pronto. De la documentation :

El identificador se cerrará al salir de withFile

Esto significa que el archivo se cerrará tan pronto como la función withFile regrese.

Debido a que hGetContents y sus amigos son flojos, no intentará leer el archivo hasta que forzado con putStrLn , pero para entonces, con withFile ya habrá cerrado el archivo.

Para resolver el problema, páselo todo a withFile :

main = withFile "test.txt" ReadMode $ /handle -> do xs <- getlines handle sequence_ $ map putStrLn xs

Esto funciona porque para cuando withFile llegue a cerrar el archivo, ya lo habría impreso.


Ellos hacen cosas completamente diferentes. openFile abre un archivo y devuelve un manejador de archivo:

openFile :: FilePath -> IOMode -> IO Handle

withFile se utiliza para ajustar un cálculo de IO que toma un identificador de archivo, asegurando que el identificador se cierre después:

withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r

En su caso, usar withFile se vería así:

main = withFile "test.txt" ReadMode $ /h -> do xs <- getlines h sequence_ $ map putStrLn xs

La versión que tiene actualmente abrirá el archivo, llamará a getlines y luego cerrará el archivo. Como getlines es flojo, no se leerá ninguna salida mientras el archivo esté abierto, y una vez que se cierre el archivo, no podrá leerlo.


Te encuentras con los obstáculos habituales de IO perezoso ... IO lento parece una excelente idea, haciendo que la transmisión fluya en un abrir y cerrar de ojos hasta que comiences a tener esos terribles problemas.

No es que su caso particular no sea una arenga para un Haskeller experimentado: este es el ejemplo de libro de texto de por qué el IO perezoso es un problema.

main = do xs <- withFile "test.txt" ReadMode getlines sequence_ $ map putStrLn xs

withFile toma un FilePath, un modo y una acción para hacer con el handle resultante de abrir este filepath con este modo. La parte interesante de withFile es que se implementa con corchete y garantía, incluso en caso de excepción, de que el archivo se cerrará después de que se ejecute la acción en el controlador. El problema aquí es que la acción en cuestión (getLines) no lee el archivo en absoluto. ¡Solo promete hacerlo cuando el contenido realmente se necesita! Esto es IO perezosa (implementado con el inseguroInterleaveIO, adivina qué significa la parte "insegura" ...). Por supuesto, para cuando se necesita este contenido (putStrLn), el identificador fue cerrado por withFile según lo prometido.

De modo que tiene varias soluciones: puede usar abrir y cerrar explícitamente (y renunciar a la excepción de seguridad), o puede usar IO perezoso pero poner cada acción tocando el contenido del archivo en el alcance protegido por withFile:

main = withFile "test.txt" ReadMode$ /h -> do xs <- getlines h mapM_ putStrLn xs

En este caso, esto no es demasiado horrible, pero debería ver que el problema puede volverse más molesto si ignora cuándo se necesitará el contenido. Lazy IO en un programa grande y complejo puede convertirse rápidamente en algo bastante molesto, y cuando la limitación de números de identificadores abiertos comienza a ser importante ... Por eso, el nuevo deporte de la comunidad Haskell es encontrar soluciones al problema del contenido de transmisión. (en lugar de leer todos los archivos en la memoria que "resuelven" el problema a costa de la distensión de la memoria, utilicen niveles a veces imposibles) sin IO perezoso. Durante un tiempo parecía que Iteratee se convertiría en la solución estándar, pero era muy complejo y difícil de entender, incluso para Haskeller experimentado, por lo que otros candidatos se han deslizado últimamente: el más prometedor o al menos exitoso en la actualidad parece ser "conduit" .


Uf, ¿nadie le dio la solución simple?

main :: IO () main = do xs <- fmap lines $ readFile "test.txt" mapM_ putStrLn xs

No use openFile + hGetContents o con withFile + hGetContents cuando puede usar readFile . Con readFile no puede dispararse en el pie cerrando el archivo demasiado pronto.