我有一个ByteString b
,其长度为4 * n(整数n),并希望在map
上使用f::Word32->Word32
函数b
(以便f
应用于“b[0..3]
”,“b[4..7]
”等。如何以高效(优雅)的方式完成这项工作?
答案 0 :(得分:4)
鉴于B.take
和B.drop
都是ByteString
操作,您可以非常有效地 chunk up O(1)
:
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
chunk :: Int -> ByteString -> [ByteString]
chunk k = takeWhile (not . B.null) . map (B.take k) . iterate (B.drop k)
然后:
\> :set -XOverloadedStrings
\> chunk 4 "abcdefghijkl"
["abcd","efgh","ijkl"]
剩下的就是将列表转换为所需类型的转换,以及最后一次调用B.concat
。
可以使用位移和左侧折叠来实现fromByteString
:
import Data.Bits (Bits, shiftL, (.|.))
fromByteString :: (Num a, Bits a) => ByteString -> a
fromByteString = B.foldl go 0
where go acc i = (acc `shiftL` 8) .|. (fromIntegral i)
然后:
\> map fromByteString $ chunk 4 "abcdefghijkl" :: [Word32]
[1633837924,1701209960,1768581996]
答案 1 :(得分:4)
一种hackish但有效的方法是convert to a storable vector(不需要副本!),映射,并转换回来:
import Data.Vector.Storable.ByteString
import qualified Data.Vector.Storable as VS
import qualified Data.ByteString as BS
mapBSChunks :: (VS.Storable a, VS.Storable b)
=> (a->b) -> BS.ByteString -> BS.ByteString
mapBSChunks f = vectorToByteString . VS.map f . byteStringToVector
根据Michael's comment,您可以在本地轻松定义这些hackish转换函数:
bytestringToVector bs = runST
( V.unsafeThaw (toByteVector bs) >>= V.unsafeFreeze . M.unsafeCast )
vectorToByteString v = runST
( V.unsafeThaw v >>= fmap fromByteVector . V.unsafeFreeze . M.unsafeCast )
虽然我依赖于一个库提供这个,特别是,因为不安全的演员有点可疑。