takeWhile
との間のクロスのようなコンジットを作成しようとしていisolate
ます。つまり、述語が保持されなくなるか、バイト制限に達するまで、入力から消費し、出力に譲ります。型シグネチャが
isolateWhile :: (Monad m) => Int -> (Word8 -> Bool) -> Conduit ByteString m ByteString
その使用例として:
{-# LANGUAGE OverloadedStrings #-}
import Data.Conduit
import qualified Data.Conduit.List as CL
import qualified Data.Conduit.Binary as CB
import Control.Monad.Trans.Class
charToWord = fromIntegral . fromEnum
example :: Int -> Char -> IO ()
example limit upTo = do
untaken <- CB.sourceLbs "Hello, world!" $= conduit $$ CB.sinkLbs
putStrLn $ "Left " ++ show untaken
where
conduit = do
taken <- toConsumer $ isolateWhile limit (/= charToWord upTo) =$ CB.sinkLbs
lift $ putStrLn $ "Took " ++ show taken
CL.map id -- pass the rest through untouched
私はそれを期待しています
ghci> example 5 'l'
Took "He"
Left "llo, world!"
ghci> example 5 'w'
Took "Hello"
Left ", world!"
ただし、 の最も単純な定義は次のisolateWhile
とおりです。
isolateWhile limit pred = CB.isolate limit =$= CB.takeWhile pred
収量
ghci> example 5 'l'
Took "He"
Left ", world!"
ghci> example 5 'w'
Took "Hello"
Left ", world!"
つまり、はを残して破棄し、をisolate
すべて使い果たします。このデータ損失は、アプリケーションにとって望ましくありません。ただし、2 番目のケースが期待どおりの結果をもたらすことは注目に値します。Hello
He
takeWhile
llo
=$=
そのようなオペランドを交換すると:
isolateWhile limit pred = CB.takeWhile pred =$= CB.isolate limit
それで
ghci> example 5 'l'
Took "He"
Left ", world!"
ghci> example 5 'w'
Took "Hello"
Left ""
これで、最初のテストは修正されましたが、2 番目のテストは壊れました。今回は、takeWhile
必要なものは何でもisolate
取り、そのサブセットを取ります。しかし、takeWhile
使用しisolate
ないものは破棄され、これは望ましくありません。
最後に、私は試しました:
isolateWhile limit pred = do
untaken <- CB.isolate limit =$= (CB.takeWhile pred >> CL.consume)
mapM_ leftover $ reverse untaken
これは実際に機能します!受け入れるものisolate
と受け入れtakeWhile
ないものはすべて によって消費されCL.consume
、 でストリームに戻されますleftover
。残念ながら、これは恐ろしいクラッジのように思えlimit
ますleftover
. それは無駄のようです。
私が考えることができる唯一の解決策は、プリミティブの観点からawait
それyield
を書くleftover
ことです。これにより、多くの問題を無駄にすることなくすべての問題が解決されますが、もっと良い方法があるはずです。takeWhile
isolate
私は何かを見逃していますか、それともこれを書くより良い方法は本当にありませんか?