我是Haskell的初学者,仍然在学习monad变压器。
我正在尝试使用streaming-bytestring库读取二进制文件,处理字节块并在处理每个块时打印结果。我相信这是流行的streaming
库,它提供了惰性字节串的替代方案。看来作者复制粘贴了lazy bytestring文档,并添加了一些任意示例。
这些示例提到runResourceT
,而没有讨论它是什么或如何使用。似乎应该在执行操作的任何流字节字符串函数上使用runResourceT
。很好,但是如果我正在读取处理块并打印它们的无限流怎么办?每次我想处理该块时都应该调用runResourceT吗?
我的代码是这样的:
import qualified Data.ByteString.Streaming as BSS
import System.TimeIt
main = timeIt $ processByteChunks $ BSS.drop 100 $ BSS.readFile "filename"
,但我不确定如何将processByteChunks
组织为遍历二进制文件的递归函数。
如果仅调用runResourceT
,它将在打印前读取无限文件,对吗?好像不好。
main = timeIt $ runResourceT $ processByteChunks $ BSS.drop 100 $ BSS.readFile "filename"
答案 0 :(得分:3)
ResourceT
monad会在您使用完资源后及时清理它们。在这种情况下,它将确保在使用流时关闭由BSS.readFile
打开的文件句柄。 (除非流确实是无限的,否则我猜不会。)
在您的应用程序中,您只想调用一次,因为您不希望在读取所有块之前关闭文件。不用担心-它与输出的时间或诸如此类无关。
这是一个应该起作用的递归processByteChunks
的示例。它将延迟读取并在延迟读取块时生成输出:
import Control.Monad.IO.Class
import Control.Monad.Trans.Resource
import qualified Data.ByteString.Streaming as BSS
import qualified Data.ByteString as BS
import System.TimeIt
main :: IO ()
main = timeIt $ runResourceT $
processByteChunks $ BSS.drop 100 $ BSS.readFile "filename"
processByteChunks :: MonadIO m => BSS.ByteString m () -> m ()
processByteChunks = go 0 0
where go len nulls stream = do
m <- BSS.unconsChunk stream
case m of
Just (bs, stream') -> do
let len' = len + BS.length bs
nulls' = nulls + BS.length (BS.filter (==0) bs)
liftIO $ print $ "cumulative length=" ++ show len'
++ ", nulls=" ++ show nulls'
go len' nulls' stream'
Nothing -> return ()