Если вы используете String IO, вы можете сделать следующее:
import System.IO
import Control.Monad
-- | Process 100 lines
process100 :: [String] -> MyData
-- whatever this function does
loop :: [String] -> [MyData]
loop lns = go [] lns
where
go acc [] = reverse acc
go acc lns = let (this, next) = splitAt 100 lns in go (process100 this:acc) next
processFile :: FilePath -> IO [MyData]
processFile f = withFile f ReadMode (fmap (loop . lines) . hGetContents)
Обратите внимание, что эта функция будет молча обрабатывать последний фрагмент, даже если он не точно 100 строк.
Пакеты, такие как bytestring и text, обычно предоставляют такие функции, как lines
и hGetContents
, поэтому вы сможете легко адаптировать эту функцию к любому из них.
Важно знать, что вы делаете с результатами обработки каждого среза, потому что вы не хотите удерживать эти данные дольше, чем необходимо. В идеале после расчета каждого среза данные будут полностью использованы и могут быть скопированы. Как правило, отдельные результаты объединяются в одну структуру данных («сворачивание»), или каждый из них рассматривается отдельно (возможно, вывод строки в файл или что-то подобное). Если это фолд, вы должны изменить «цикл», чтобы он выглядел так:
loopFold :: [String] -> MyData -- assuming there is a Monoid instance for MyData
loopFold lns = go mzero lns
where
go !acc [] = acc
go !acc lns = let (this, next) = splitAt 100 lns in go (process100 this `mappend` acc) next
Функция loopFold
использует шаблоны взрыва (включается с помощью прагмы «LANGUAGE BangPatterns») для принудительной оценки «MyData». В зависимости от того, что такое MyData, вам может понадобиться использовать deepseq
, чтобы убедиться, что оно полностью оценено.
Если вместо этого вы пишете каждую строку для вывода, оставьте loop
как есть и измените processFile
:
processFileMapping :: FilePath -> IO ()
processFileMapping f = withFile f ReadMode pf
where
pf = mapM_ (putStrLn . show) <=< fmap (loop . lines) . hGetContents
Если вы заинтересованы в обработке стилей перечислителя / итератора, это довольно простая проблема. Я не могу привести хороший пример, не зная, какую работу process100
делает, но это будет включать enumLines
и take
.
Необходимо ли обрабатывать ровно 100 строк за раз, или вы просто хотите обрабатывать блоками для повышения эффективности? Если это последнее, не беспокойтесь об этом. Скорее всего, вам лучше обрабатывать по одной строке за раз, используя либо реальную функцию свертывания, либо функцию, аналогичную processFileMapping.