【问题标题】:Better way to write loops in IO在 IO 中编写循环的更好方法
【发布时间】:2015-08-31 19:00:39
【问题描述】:

给定一个 png 文件,我正在尝试获取其块的偏移量和大小的列表。

简而言之,png files 由块组成,每个块由三个 4 字节字段加上一个可变长度字段(块的数据字段)组成。数据字段的大小存储在第一个 4 字节字段中(称为“长度”字段)。

因此,给定当前块的偏移量和大小,(ofs, sz),就可以得出下一个块的偏移量和大小,(ofs', sz'):

ofs' = ofs + sz

在偏移量 = ofs' 处读取 sz'

给定初始块的偏移量和大小,在 png 文件中总是 (0, 8),可以循环遍历文件直到到达其末尾。我就是这样做的:

import Data.Word
import qualified Data.ByteString.Lazy as BS
import Data.Binary.Get

size :: BS.ByteString -> Int -> IO (Int)
size bytes offset = do
    let ln = runGet (do skip offset
                        l <- getWord32be
                        return l)
                    bytes
    return $ 3*4 + fromIntegral ln

offsetSizes :: Int -> BS.ByteString -> [(Int, Int)] -> IO [(Int, Int)]
offsetSizes fLen bytes oss = do
        let (offset, sz) = last oss
            offset' = offset + sz
        sz' <- size bytes offset'
        let nextOffset = offset' + sz'
        if nextOffset < fLen then offsetSizes fLen bytes $ oss ++ [(offset', sz')]
                              else return oss
main = do
    contents <- BS.readFile "myfile.png"
    let fLen = fromIntegral $ BS.length contents :: Int

    ofszs <- offsetSizes fLen contents [(0,8)]
    putStrLn $ "# of chunks: " ++ (show $ length ofszs)
    putStrLn $ "chunks [(offset,size)]: " ++ show ofszs

我的问题:我对循环不太满意。我想知道在 Haskell 中是否有更惯用的方法来实现相同的目标?

【问题讨论】:

  • 我会在offsetSizes 中使用unfoldrM 之类的东西;另外,您可以写let ln = runGet (skip offset &gt;&gt; getWord32be) bytes 使其更简单。
  • 循环还不错。相反,看起来很糟糕的是重复的oss ++ [(offset', sz')],这是低效的。最好使用(offset', sz') : oss 构建一个反向列表,然后在循环结束后最终将其反向。这也可以让你避免last oss,这很慢。
  • 谢谢你的建议,我试试看。

标签: loops haskell


【解决方案1】:

offsetSizes 被反复输入某些状态((offset, sz) 对)以产生新的对或完成。所有创建的对都收集到一个列表中。

这个递归方案被unfoldrMmonad-loops包中捕获,允许你写offsetSizes

offsetSizes :: Int -> BS.ByteString -> IO [(Int, Int)]
offsetSizes fLen bytes = unfoldrM step (0, 8)
  where
    step (offset, sz) = do
        let offset' = offset + sz
        sz' <- size bytes offset'
        let state' = (offset', sz')
        return $ if offset' + sz' < fLen then Just (state', state') else Nothing

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多