¿Cómo puedo leer múltiples archivos como una sola ByteString perezosamente con memoria constante?
Si desea un uso constante de la memoria, necesita Data.ByteString.Lazy
. Un estricto ByteString
no se puede leer perezosamente, y requeriría la memoria O(sum of filesizes)
.
Para un no demasiado gran número de archivos, la simple lectura de todos ellos (D.B.L.readFile
lee muy lentamente) y la concatenación de los resultados es buena,
import qualified Data.ByteString.Lazy as L
readFiles :: [FilePath] -> IO L.ByteString
readFiles = fmap L.concat . mapM L.readFile
El mapM L.readFile
abrirá los archivos, pero sólo leer el contenido de cada uno archivo cuando se lo exija.
Si el número de archivos es grande, por lo que podría agotarse el límite de identificadores de archivos abiertos permitidos por el sistema operativo para un solo proceso, necesita algo más complicado. Puede cocinar su propia versión lenta de mapM
,
import System.IO.Unsafe (unsafeInterleaveIO)
mapM_lazy :: [IO a] -> IO [a]
mapM_lazy [] = return []
mapM_lazy (x:xs) = do
r <- x
rs <- unsafeInterleaveIO (mapM_lazy xs)
return (r:rs)
de modo que sólo se abrirá cada archivo cuando se necesitan sus contenidos, cuando los archivos leído anteriormente puede ya ser cerrado. Hay una pequeña posibilidad de que eso todavía se encuentre con los límites de recursos, ya que no está garantizado el momento de cerrar los identificadores.
O puede usar su favorito iteratee
, enumerator
, conduit
o cualquier paquete que resuelva el problema de forma sistemática. Cada uno de ellos tiene sus propias ventajas y desventajas con respecto a los demás y, si está codificado correctamente, elimina la posibilidad de golpear accidentalmente el límite de recursos.
Gracias, debería haber mencionado que ya estaba usando Data.ByteString.Lazy. Esto funcionó muy bien con el uso de memoria aumentando un poco con cada manejador abierto. También gracias por señalar los paquetes adicionales. Acabo de empezar a aprender Haskell y todavía no me he enterado. –