【问题标题】:Can I read n files lazily as a single IO operation in Haskell?我可以在 Haskell 中作为单个 IO 操作懒惰地读取 n 个文件吗?
【发布时间】:2012-10-01 13:57:17
【问题描述】:

如何使用恒定内存懒惰地将多个文件作为单个 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 的方法。

【问题讨论】:

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


    【解决方案1】:

    如何使用恒定内存懒惰地将多个文件作为单个 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 或任何以系统方式解决问题的软件包。相对于其他的,它们中的每一个都有自己的优点和缺点,如果编码正确,可以消除意外达​​到资源限制的可能性。

    【讨论】:

    • 谢谢,我应该提到我已经在使用 Data.ByteString.Lazy。这很好用,每个打开的句柄都会增加一点内存使用量。也感谢您指出额外的包,我刚开始学习 Haskell,还没有遇到过。
    【解决方案2】:

    我假设您使用的是惰性字节字符串(来自Data.ByteString.Lazy)。可能还有其他方法可以做到这一点,但一种选择是简单地使用concat :: [ByteString] -&gt; 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
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-05
      • 1970-01-01
      • 2011-07-19
      • 2021-03-24
      • 2016-02-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多