我可以懒惰地读取n个文件作为Haskell中的单个IO操作吗?

时间:2012-10-01 13:57:17

标签: haskell file-io io lazy-evaluation lazy-sequences

如何使用常量内存懒惰地将多个文件作为单个ByteString读取?

readFiles :: [FilePath] -> IO ByteString

我目前有以下实现,但从我从分析中看到的以及我的理解,我将以内存中n-1个文件结束。

readFiles = foldl1 joinIOStrings . map ByteString.readFile
    where joinIOStrings ml mr = do
                                l <- ml
                                r <- mr
                                return $ l `ByteString.append` r

我理解这里的缺陷是我正在应用IO操作然后重新打包它们,所以我认为我需要的是一种替换foldl1 joinIOStrings而不应用它们的方法。

2 个答案:

答案 0 :(得分:7)

  

如何使用常量内存懒惰地将多个文件作为单个ByteString读取?

如果您想要不断使用内存,则需要Data.ByteString.Lazy。严格的ByteString不能懒惰地阅读,并且需要O(sum of filesizes)个记忆。

对于不太多的文件,只需读取它们(D.B.L.readFile懒惰地读取)并连接结果就好了,

import qualified Data.ByteString.Lazy as L

readFiles :: [FilePath] -> IO L.ByteString
readFiles = fmap L.concat . mapM L.readFile

mapM L.readFile将打开文件,但只在需要时才读取每个文件的内容。

如果文件数量很大,那么操作系统允许单个进程允许的打开文件句柄限制可能会耗尽,您需要更复杂的东西。你可以自己制作懒惰版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)

这样,只有当需要其内容时才会打开每个文件,而以前读取的文件已经可以关闭。由于关闭句柄的时间无法保证,因此仍有可能仍然遇到资源限制。

或者,您可以使用自己喜欢的iterateeenumeratorconduit或任何以系统方式解决问题的软件包。它们中的每一个都有其自身的优点和缺点,如果编码正确,则消除了意外达到资源限制的可能性。

答案 1 :(得分:1)

我假设您正在使用延迟字节字符串(来自Data.ByteString.Lazy)。可能有其他方法可以做到这一点,但一种选择是简单地使用concat :: [ByteString] -> ByteString

import Control.Monad
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as ByteString

readFiles :: [FilePath] -> IO ByteString
readFiles = fmap ByteString.concat . mapM ByteString.readFile

(注意:我没有时间测试代码,但阅读文档说这应该有效)