Mi contexto es la bioinformática, la secuenciación de próxima generación en particular, pero el problema es genérico; entonces usaré un archivo de registro como ejemplo.Haskell: ¿Puedo realizar varios pliegues sobre la misma lista perezosa sin mantener la lista en la memoria?
El archivo es muy grande (Gigabytes grande, comprimido, por lo que no cabe en la memoria), pero es fácil de analizar (cada línea es una entrada), por lo que fácilmente puede escribir algo como:
parse :: Lazy.ByteString -> [LogEntry]
Ahora, tengo muchas estadísticas que me gustaría calcular a partir del archivo de registro. Es más fácil de escribir funciones separadas, tales como:
totalEntries = length
nrBots = sum . map fromEnum . map isBotEntry
averageTimeOfDay = histogram . map extractHour
Todos estos son de la forma foldl' k z . map f
.
El problema es que si trato de usarlos de la manera más natural, como
main = do
input <- Lazy.readFile "input.txt"
let logEntries = parse input
totalEntries' = totalEntries logEntries
nrBots' = nrBots logEntries
avgTOD = averageTimeOfDay logEntries
print totalEntries'
print nrBots'
print avgTOD
Esto le asigne toda la lista en la memoria, lo cual no es lo que quiero. Quiero que los pliegues se hagan de forma sincrónica, para que las celdas de cons puedan ser recogidas. Si calculo solo una estadística, esto es lo que sucede.
Puedo escribir una sola función grande que hace esto, pero es un código que no se puede componer.
Alternativamente, que es lo que he estado haciendo, ejecuto cada pase por separado, pero esto vuelve a cargar & descomprime el archivo cada vez.
¿Por qué no hacer 'logAnalysers :: [(K, Z, F)], donde' 'K, Z, F' son los tipos de las funciones' k, z, f' en su ejemplo? Luego se convierte en código "composable", de alguna manera, si tiene un doblez único que usa la lista. – dflemstr
@dflemstr los tipos intermedios no son siempre los mismos :( – luispedro
Usted * podría * hacer 'logAnalysers :: [forall abc. (B -> c -> b, c, a -> b)]', que permitiría diferentes tipos ... – dflemstr