1

バイト文字列に作用する2つの関数があります。1 つ目は Data.List のサイクル関数のバイト文字列バージョンで、2 つ目の関数はバイト文字列をローテーションします。

ファイルを読み取り、その入力を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します。膨大な量のデータに Lazy ByteStrings を使用する場合があるため、ライブラリにはこれを可能にする長さパラメーターが必要です。)nInt64Int
  2. nまたは(より良い) modを減らして、無限に多くの代わりにB.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!これは計算されます。これは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

より明確に読み取れます。takedropおよびsplitAtはすべてO(1)ByteStringあるため、効率には大きな違いはありませんが、splitAtはよりクリーンに感じられます。

于 2012-11-07T07:07:10.623 に答える