1

我有两个作用于字节字符串的两个函数。第一个是 Data.List 中循环函数的字节串版本,第二个函数旋转字节串。

当我读取文件并将其输入发送到 rotateBytes 函数时,它会输出一个引号,然后我需要按 Control-C 手动停止该函数。这是我的代码中的错误还是 ghc 中的错误?如何修复?

import qualified Data.ByteString as B

-- Cycle function for binary data
cycleBytes :: B.ByteString -> B.ByteString 
cycleBytes xs 
    | B.null xs = error "cycleBytes: empty list"
    | otherwise = xs' where xs' = xs `B.append` xs'

-- Rotate function for binary data
rotateBytes :: B.ByteString -> Int -> B.ByteString
rotateBytes xs n = B.take (B.length xs) $! B.drop (B.length xs + n) $! cycleBytes xs

目前使用函数是这样的:

*Main> B.readFile "test.dat" >>= (\x -> return $ rotateBytes x 3)
"
^CInterrupted.
4

1 回答 1

3

ByteString 并不懒惰,因此无法应对来自cycleBytes. ("你得到的是因为在打印结果时,它可以懒惰地获取输出的第一个字符而不用担心其余部分,但是然后尝试cycleBytes在打印其他任何内容之前计算它得到的无限 ByteString。无穷大和严格的评估不要'混合。)

  1. Data.ByteString.Lazy改为导入(并fromIntegral在您的 上使用n,因为它Int64不需要Int;您可能希望将 Lazy ByteStrings 用于大量数据,因此您的库需要一个允许这样做的长度参数。)
  2. 或(更好)减少nmodB.length xs并仅使用 B.append xs xs而不是无限多。

解决方案 1 看起来像

import qualified Data.ByteString.Lazy as B

-- Cycle function for binary data
cycleBytes :: B.ByteString -> B.ByteString 
cycleBytes xs 
    | B.null xs = error "cycleBytes: empty list"
    | otherwise = xs' where xs' = xs `B.append` xs'


-- Rotate function for binary data
rotateBytes :: B.ByteString -> Int -> B.ByteString
rotateBytes xs n = B.take (B.length xs) 
                 $ B.drop (B.length xs + fromIntegral n) 
                 $ cycleBytes xs

如果test.dat包含Hello Mum!this 将计算Chunk "lo Mum!" (Chunk "Hel" Empty),因为它是惰性构造的,并且只有在强制时才会合并。

解决方案 2 看起来像

rotateBytes :: B.ByteString -> Int -> B.ByteString
rotateBytes xs n = 
    let len = B.length xs
        n' = n `mod` len in
     B.take len $! B.drop n' $! B.append xs xs

解决方案 2 更好,部分原因是您可以保持一切严格(我假设您正在尝试这样做),但没有cycleBytes.

Ben Millwood 建议用类似的东西代替它

rotateBytes :: B.ByteString -> Int -> B.ByteString
rotateBytes xs n = 
    let n' = n `mod` B.length xs 
        (beginning,end) = B.splitAt n' xs in
    B.append end beginning

读起来更清楚。take,drop并且splitAt都是O(1) in ByteString,所以它对效率没有太大影响,但splitAt确实感觉更干净。

于 2012-11-07T07:07:10.623 回答