[Haskell-cafe] 'Progress bar' enumeratee
David Hotham
david.hotham at blueyonder.co.uk
Wed Apr 6 14:38:57 CEST 2011
An error slipped into the version below. The line:
E.checkDone (E.continue . dotIn (count - len))
should read
E.checkDone (E.continue . dotIn (need - len))
"David Hotham" <david.hotham at blueyonder.co.uk> wrote in message
news:inhd6m$h2n$1 at dough.gmane.org...
> Hello,
>
> I've spent some time over the last couple of days trying to write an
> enumeratee that prints a "." every n bytes (with obvious intended use as a
> progress tracker). Seems like it oughtn't be hard, but it has been a
> steep learning curve...
>
> I have come up with something that seems to do the job but I don't know
> that I'm completely happy with it (or even that I completely understand
> it, to be honest).
>
> If anyone more expert would be kind enough either to reassure me that I'm
> doing it right or - more likely - to offer improvements / suggestions on
> what obvious simplifications I have overlooked, I'd be grateful.
>
> Thanks
>
> David
>
>
> import qualified Data.ByteString.Char8 as B
> import qualified Data.ByteString.Lazy.Char8 as BL
> import qualified Data.Enumerator as E
> import qualified Data.Enumerator.Binary as EB
> import Control.Monad.IO.Class (liftIO, MonadIO)
> import System.IO (hFlush, stdout)
>
>
> dotEvery :: MonadIO m => Integer -> E.Enumeratee B.ByteString B.ByteString
> m b
> dotEvery count = E.checkDone $ E.continue . dotIn count where
> dotIn need k E.EOF = E.yield (E.Continue k) E.EOF
> dotIn need k (E.Chunks []) = E.continue (dotIn need k)
> dotIn need k (E.Chunks xs) = iter where
> lazy = BL.fromChunks xs
> len = toInteger $ BL.length lazy
> iter = if len < need
> then k (E.Chunks xs) E.>>==
> E.checkDone (E.continue . dotIn (count - len))
> else let (x1, x2) = BL.splitAt (fromInteger need) lazy
> s1 = E.Chunks $ BL.toChunks x1
> s2 = E.Chunks $ BL.toChunks x2
> enumee = E.checkDoneEx s2 (\k' -> dotIn count k' s2)
> in E.Iteratee $ do newStep <- E.runIteratee $ k s1
> liftIO $ putStr "." >> hFlush stdout
> E.runIteratee $ enumee newStep
>
>
>
> PS Implementations which involve "EB.take count" seem to me
> unsatisfactory; one surely oughtn't need to have a large buffer to solve
> this problem
> PPS I did find an implementation via mapAccumM which I consider pleasing
> enough from an aesthetic point of view - but which runs 30x slower
>
> dotAt :: MonadIO m => Integer -> Integer -> Word8 -> m (Integer, Word8)
> dotAt n s w | s >= n = do liftIO $ putStr "." >> hFlush stdout
> return (1, w)
> | otherwise = return (s+1, w)
>
> dotEvery' :: MonadIO m => Integer -> E.Enumeratee B.ByteString
> B.ByteString m b
> dotEvery' n = EB.mapAccumM (dotAt n) 1
More information about the Haskell-Cafe
mailing list