Я пытаюсь создать канал, нечто среднее между 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
съест все 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 ""
Теперь я исправил первый тест, но сломал второй! На этот раз 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
a> как takeWhile
и isolate
сами написали. Хотя это решило бы все проблемы без больших потерь, похоже, должен быть лучший способ.
Я что-то упустил, или действительно нет лучшего способа написать это?
zip
a> вместе с источником, подобным[0..]
, а затем сделать так, чтобы ваш предикатtakeWhile
рассматривал и индекс, и значение? - person Daniel Wagner   schedule 21.10.2013ByteString
s, а не отдельные байты. - person icktoofay   schedule 21.10.2013