使用Data.Binary.Get构建可变向量

时间:2014-07-02 12:15:06

标签: haskell

我正在尝试将二进制文件解析为haskell向量。我可以将我的文件加载到常规列表中,但由于每个文件有超过10000000个元素,因此我的表现非常糟糕。

要解析二进制文件,我使用Data.Binary.GetData.Binary.IEEE754,因为我打算读取浮点值。我正在尝试将我的矢量构建为Mutable,然后将其冻结。

我最终遇到问题,因为Get不是Control.Monad.Primitive.PrimMonad的实例,对我来说这看起来很模糊。

import qualified Data.ByteString.Lazy        as B
import qualified Data.Vector.Unboxed.Mutable as UM
import qualified Data.Vector.Unboxed         as U
import Data.Binary.Get
import Data.Binary.IEEE754

type MyVectorOfFloats = U.Vector Float

main = do
    -- Lazyly read the content of the file as a ByteString
    file_content <- B.readFile "vec.bin"
    -- Parse the bytestring and get the vector
    vec <- runGet (readWithGet 10) file_content :: MyVectorOfFloats
    -- Do something usefull with it...
    return ()


readWithGet :: Int
            -> Get MyVectorOfFloats -- ^ Operates in the Get monad
readWithGet n = do
    -- Initialize a mutable vector of the desired size
    vec <- UM.new n
    -- Initialize the vector with values obtained from the Get monad
    fill vec 0
    -- Finally return freezed version of the vector
    U.unsafeFreeze vec
  where
    fill v i
        | i < n = do
            -- Hopefully read one fload32 from the Get monad
            f <- getFloat32le
            -- place the value inside the vector
            -- In the real situation, I would do more complex decoding with
            -- my float value f
            UM.unsafeWrite v i f
            -- and go to the next value to read
            fill v (i + 1)
        | otherwise = return ()

上面的例子非常简单,在我的情况下我有run-length喜欢解码,但问题保持不变。

首先,我选择的库是否足以供我使用?我目前并不真正需要内存中的所有向量。我可以操作块。来自pipe或Conduit的东西看起来很有趣。

我是否必须让Get Control.Monad.Primitive.PrimMonad的实例做我想做的事情?

我想我可以尝试做一些展开模式来构建没有可变状态的向量。

1 个答案:

答案 0 :(得分:1)

如果您不需要同时获取所有数据,则应该使用流式库。 (如果事情很简单,你可能会使用懒惰的I / O.)

您的错误来自于您已声明'do'块在Get monad中运行,但UM.new只能在ST或IO monad中运行。你需要将readWithGet更改为IO或ST monad,尽管它仍然可以在“引擎盖下”使用Get monad(并在内部调用runGet)。

以下是我在Get和Parser之间进行转换的方法(来自pipe-parse):

type GetParser m a = Parser ByteString (EitherT GetFail m) a

data GetFail = GetFail !ByteOffset String

get2parser :: Get a -> GetParser m a
get2parser = decoder2parser . runGetIncremental

decoder2parser :: Decoder a -> GetParser m a
decoder2parser (Fail r off err) = unDraw r >> lift (left $ GetFail off err)
decoder2parser (Partial cont) = draw >>= decoder2parser . cont
decoder2parser (Done r _ a) = unDraw r >> return a