Haskell Data.Binary:这不应该是懒惰的吗?而我如何让它变得懒惰?

时间:2013-08-22 20:36:51

标签: haskell binary lazy-evaluation

我想为蒙特卡罗模拟处理几百个二进制数据块(“场景”)。每个场景包含100万个浮点数。以下是我为场景数据创建虚拟二进制文件的方法:

import Data.Binary 
import qualified Data.ByteString.Lazy as B
import Data.Array.Unboxed

scenSize = 1000000
scens = 100

main = do
    let xs = array (1,scenSize) [(i, 0.0) | i <- [1..scenSize]] :: UArray Int Float
    let l = take scens $ Prelude.repeat xs
    B.writeFile "bintest.data" (encode l)
    return ()

这很好用。现在我想处理这些场景。由于可能存在很多场景(场景= 1000左右),因此处理应该一次懒得一个块。我尝试了decodeFile,但这似乎不起作用:

import Data.Binary 
import qualified Data.Array.IArray as IA
import Data.Array.Unboxed as A

main = do
    bs <- decodeFile "bintest.data" :: IO [UArray Int Float]
    mapM_ doStuff bs
    return ()

doStuff b = 
    Prelude.putStrLn $ show $ b IA.! 100000

该程序似乎首先将所有数据加载到内存中,然后在运行结束时打印所有数字。它在我的32位Ubuntu机器上使用了很多内存和崩溃的scens = 500。

我做错了什么?是否有一种简单的方法可以使程序懒惰地运行?

1 个答案:

答案 0 :(得分:4)

decodeFile不是懒惰的,只需查看源-it调用decodeOrFail,它本身必须解析整个文件以确定成功或失败。

编辑:

所以我认为在原版binary中工作的东西现在已经被破坏了(阅读:它现在是一个非懒惰的记忆猪)。我怀疑最好的一个解决方案是使用惰性readFilerunGetIncremental然后手动将块推入解码器:

import Data.Binary
import Data.Binary.Get
import Data.ByteString.Lazy as L
import Data.ByteString as B
import qualified Data.Array.IArray as IA
import Data.Array.Unboxed as A

main = do
    bs <- getListLazy `fmap` L.readFile "bintest2.data"
    mapM_ doStuff bs
    return ()

doStuff b = print $ b IA.! 100000

重要的是:

getListLazy :: L.ByteString -> [UArray Int Float]
getListLazy lz = go decodeUArray (L.toChunks lz)
  where
    go :: Decoder (UArray Int Float) -> [B.ByteString] -> [UArray Int Float]
    go _ []       = []
    go dec (b:bs) =
      case pushChunk dec b of
        Done b' o a -> a : go decodeUArray (b' : bs)
        Partial f   -> case bs of
                          (x:xs) -> go (f $ Just x) xs
                          []     -> []
        Fail _ _ s -> error s -- alternatively use '[]'

    decodeUArray :: Decoder (UArray Int Float)
    decodeUArray = runGetIncremental get

请注意,这个解决方案没有麻烦解码然后通过解码器管理列表长度 - 我只是更改了生成器代码以输出大量数组而不是数组列表。

为了避免像这样的代码,我认为管道是可行的方法。