2

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 番目のケースが期待どおりの結果をもたらすことは注目に値します。HelloHetakeWhilello

=$=そのようなオペランドを交換すると:

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ことです。これにより、多くの問題を無駄にすることなくすべての問題が解決されますが、もっと良い方法があるはずです。takeWhileisolate

私は何かを見逃していますか、それともこれを書くより良い方法は本当にありませんか?

4

1 に答える 1