Для обработки ввода по частям я бы использовал перечислитель .
import Data.Enumerator
import Data.Enumerator.Binary (enumFile)
Используем строки байтов
import Data.ByteString as BS
и IO
import Control.Monad.Trans (liftIO)
import Control.Monad (mapM_)
import System (getArgs)
Ваша основная функция может выглядеть следующим образом:
main =
do (filepath:_) <- getArgs
let destination
run_ $ enumFile filepath $$ writeFile (filepath ++ ".cpy")
enumFile читает 4096 байт на блок и передает их в writeFile, который записывает их.
enumWrite определяется как:
enumWrite :: FilePath -> Iteratee BS.ByteString IO ()
enumWrite filepath =
do liftIO (BS.writeFile filepath BS.empty) -- ensure the destination is empty
continue step
where
step (Chunks xs) =
do liftIO (mapM_ (BS.appendFile filepath) xs)
continue step
step EOF = yield () EOF
Как вы можете видеть, функция step берет куски строк байтов и добавляет их
в файл назначения. Эти чанки имеют тип Stream BS.Bytestring, где
Поток определяется как:
data Stream a = Chunks [a] | EOF
На шаге EOF завершается, уступая ().
Для более подробного прочтения, я лично рекомендую Майклу
Snoymans учебник
цифры
$ time ./TestCopy 5MB
./TestCopy 5MB 2,91s user 0,32s system 96% cpu 3,356 total
$ time ./TestCopy2 5MB
./TestCopy2 5MB 0,04s user 0,03s system 93% cpu 0,075 total
Это довольно улучшение. Теперь для реализации вашего фолда вы, вероятно, захотите написать Enumeratee, который используется для преобразования входного потока. К счастью, в пакете перечислителя уже определена функция карты, которую можно изменить в соответствии с вашими потребностями, то есть ее можно изменить для переноса состояния.
На построение промежуточного результата
Вы создаете wordsList в обратном порядке и затем обращаетесь к нему. Я думаю, списки различий работают лучше, потому что добавления занимают только O (1) времени из-за того, что добавление является только композицией функций. Я не уверен, занимают ли они больше места все же. Вот приблизительный эскиз списков различий:
type DList a = [a] -> [a]
emptyList :: DList a
emptyList = id
snoc :: DList a -> a -> DList a
snoc dlist a = dlist . (a:)
toList :: DList a -> [a]
toList dlist = dlist []
Этот ответ, вероятно, больше не нужен, но я добавил его для полноты.