iteratee 0.8.0.1 → 0.8.0.2
raw patch · 5 files changed
+86/−17 lines, 5 filesPVP: minor bump suggested
API additions: PVP suggests at least a minor version bump
API changes (from Hackage documentation)
+ Data.Iteratee.ListLike: group :: (ListLike s el, Monad m, Nullable s) => Int -> Enumeratee s [s] m a
+ Data.Iteratee.ListLike: groupBy :: (ListLike s el, Monad m, Nullable s) => (el -> el -> Bool) -> Enumeratee s [s] m a
Files
- CONTRIBUTORS +1/−0
- iteratee.cabal +1/−1
- src/Data/Iteratee/ListLike.hs +55/−0
- tests/benchmarks.hs +11/−16
- tests/testIteratee.hs +18/−0
CONTRIBUTORS view
@@ -2,6 +2,7 @@ Oleg Kiselyov Gregory Collins+Nick Ingolia Brian Lewis John Lato Antoine Latter
iteratee.cabal view
@@ -1,5 +1,5 @@ name: iteratee-version: 0.8.0.1+version: 0.8.0.2 synopsis: Iteratee-based I/O description: The Iteratee monad provides strict, safe, and functional I/O. In addition
src/Data/Iteratee/ListLike.hs view
@@ -29,6 +29,8 @@ ,mapStream ,rigidMapStream ,filter+ ,group+ ,groupBy -- ** Folds ,foldl ,foldl'@@ -362,6 +364,59 @@ | True = idone (LL.filter p xs) mempty step _ = f' {-# INLINE filter #-}++-- |Creates an 'enumeratee' in which elements from the stream are+-- grouped into \sz\-sized blocks. The outer stream is completely+-- consumed and the final block may be smaller than \sz\.+group :: (LL.ListLike s el, Monad m, Nullable s) => + Int -> Enumeratee s [s] m a+group sz iinit = liftI $ go iinit LL.empty+ where go icurr pfx (Chunk s) = case gsplit (pfx `LL.append` s) of + (full, partial) | LL.null full -> liftI $ go icurr partial+ | otherwise -> do inext <- lift $ enumPure1Chunk full icurr+ liftI $ go inext partial+ go icurr pfx (EOF mex) + | LL.null pfx = lift . enumChunk (EOF mex) $ icurr+ | otherwise = do inext <- lift $ enumPure1Chunk (LL.singleton pfx) icurr + lift . enumChunk (EOF mex) $ inext+ gsplit ls = case LL.splitAt sz ls of+ (g, rest) | LL.null rest -> if LL.length g == sz+ then (LL.singleton g, LL.empty)+ else (LL.empty, g)+ | otherwise -> let (grest, leftover) = gsplit rest+ g' = g `LL.cons` grest+ in g' `seq` (g', leftover)+{-# INLINE group #-}++-- |Creates an 'enumeratee' in which elements are grouped into+-- contiguous blocks that are equal according to a predicate.+-- +-- The analogue of @List.groupBy#+ +groupBy :: (LL.ListLike s el, Monad m, Nullable s) =>+ (el -> el -> Bool) -> Enumeratee s [s] m a+groupBy same iinit = liftI $ go iinit LL.empty+ where go icurr pfx (Chunk s) = case gsplit (pfx `LL.append` s) of+ (full, partial)+ | LL.null full -> liftI $ go icurr partial+ | otherwise -> do inext <- lift . enumPure1Chunk full $ icurr+ liftI $ go inext partial+ go icurr pfx (EOF mex) + | LL.null pfx = lift . enumChunk (EOF mex) $ icurr+ | otherwise = do inext <- lift . enumPure1Chunk (LL.singleton pfx) $ icurr+ lift . enumChunk (EOF mex) $ inext+ gsplit ll | LL.null ll = (LL.empty, LL.empty)+ | otherwise = let groups = llGroupBy same ll+ full = LL.init groups+ partial = LL.last groups+ in full `seq` partial `seq` (full, partial)+ llGroupBy eq l -- Copied from Data.ListLike, avoid spurious (Eq el) constraint+ | LL.null l = LL.empty+ | otherwise = LL.cons (LL.cons x ys) (llGroupBy eq zs)+ where (ys, zs) = LL.span (eq x) xs+ x = LL.head l+ xs = LL.tail l+{-# INLINE groupBy #-} -- ------------------------------------------------------------------------ -- Folds
tests/benchmarks.hs view
@@ -77,8 +77,7 @@ dropbench = makeGroup "drop" $ drop0 : dropBenches lengthbench = makeGroup "length" listBenches takebench = makeGroup "take" $ take0 : takeBenches---takeRbench = makeGroup "takeR" $ takeR0 : takeRBenches-takeRbench = makeGroup "takeR" []+takeUpTobench = makeGroup "takeUpTo" takeUpToBenches mapbench = makeGroup "map" $ mapBenches convbench = makeGroup "convStream" convBenches miscbench = makeGroup "other" miscBenches@@ -90,15 +89,15 @@ dropbenchbs = makeGroupBS "drop" dropBenches lengthbenchbs = makeGroupBS "length" listBenches takebenchbs = makeGroupBS "take" takeBenches-takeRbenchbs = makeGroupBS "takeR" takeRBenches+takeUpTobenchbs = makeGroupBS "takeUpTo" takeUpToBenches mapbenchbs = makeGroupBS "map" mapBenches convbenchbs = makeGroupBS "convStream" convBenches miscbenchbs = makeGroupBS "other" miscBenches -allListBenches = bgroup "list" [listbench, streambench, breakbench, headsbench, dropbench, lengthbench, takebench, takeRbench, mapbench, convbench, miscbench]+allListBenches = bgroup "list" [listbench, streambench, breakbench, headsbench, dropbench, lengthbench, takebench, takeUpTobench, mapbench, convbench, miscbench] -allByteStringBenches = bgroup "bytestring" [listbenchbs, streambenchbs, breakbenchbs, headsbenchbs, dropbenchbs, lengthbenchbs, takebenchbs, takeRbenchbs, mapbenchbs, convbenchbs, miscbenchbs]+allByteStringBenches = bgroup "bytestring" [listbenchbs, streambenchbs, breakbenchbs, headsbenchbs, dropbenchbs, lengthbenchbs, takebenchbs, takeUpTobenchbs, mapbenchbs, convbenchbs, miscbenchbs] list0 = makeList "list one go" deepseq list1 = BDIter1 "stream2list one go" (flip deepseq ()) stream2list@@ -158,17 +157,13 @@ take6 = idN "take length long chunked" (I.joinI $ I.take 1000 I.length) takeBenches = [take1, take2, take3, take4, take5, take6] -{--takeR0 = makeList "take length of list long" (Prelude.length . Prelude.take 1000)-takeR1 = id1 "takeR head short one go" (I.joinI $ I.take 20 I.head)-takeR2 = id1 "takeR head long one go" (I.joinI $ I.takeR 1000 I.head)-takeR3 = idN "takeR head short chunked" (I.joinI $ I.takeR 20 I.head)-takeR4 = idN "takeR head long chunked" (I.joinI $ I.takeR 1000 I.head)-takeR5 = id1 "takeR length long one go" (I.joinI $ I.takeR 1000 I.length)-takeR6 = idN "takeR length long chunked" (I.joinI $ I.takeR 1000 I.length)-takeRBenches = [takeR1, takeR2, takeR3, takeR4, takeR5, takeR6]--}-takeRBenches = []+takeUpTo1 = id1 "takeUpTo head short one go" (I.joinI $ I.take 20 I.head)+takeUpTo2 = id1 "takeUpTo head long one go" (I.joinI $ I.takeUpTo 1000 I.head)+takeUpTo3 = idN "takeUpTo head short chunked" (I.joinI $ I.takeUpTo 20 I.head)+takeUpTo4 = idN "takeUpTo head long chunked" (I.joinI $ I.takeUpTo 1000 I.head)+takeUpTo5 = id1 "takeUpTo length long one go" (I.joinI $ I.takeUpTo 1000 I.length)+takeUpTo6 = idN "takeUpTo length long chunked" (I.joinI $ I.takeUpTo 1000 I.length)+takeUpToBenches = [takeUpTo1, takeUpTo2, takeUpTo3, takeUpTo4, takeUpTo5, takeUpTo6] map1 = id1 "map length one go" (I.joinI $ I.rigidMapStream id I.length) map2 = idN "map length chunked" (I.joinI $ I.rigidMapStream id I.length)
tests/testIteratee.hs view
@@ -14,6 +14,7 @@ import qualified Data.Iteratee.Char as IC import qualified Data.Iteratee as Iter import Data.Functor.Identity+import qualified Data.List as List (groupBy, unfoldr) import Data.Monoid import qualified Data.ListLike as LL @@ -216,6 +217,21 @@ == runner2 (enumPure1Chunk xs $ takeUpTo n stream2list) where types = xs :: [Int] +prop_group xs n = n > 0 ==>+ runner2 (enumPure1Chunk xs $ Iter.group n stream2list)+ == runner1 (enumPure1Chunk groups stream2list)+ where types = xs :: [Int]+ groups :: [[Int]]+ groups = List.unfoldr groupOne xs+ where groupOne [] = Nothing+ groupOne elts@(_:_) = Just . splitAt n $ elts+ +prop_groupBy xs m = m > 0 ==>+ runner2 (enumPure1Chunk xs $ Iter.groupBy pred stream2list)+ == runner1 (enumPure1Chunk (List.groupBy pred xs) stream2list)+ where types = xs :: [Int]+ pred z1 z2 = (z1 `mod` m == z2 `mod` m)+ -- --------------------------------------------- -- Data.Iteratee.Char @@ -276,6 +292,8 @@ ,testProperty "take" prop_take ,testProperty "take (finished iteratee)" prop_take2 ,testProperty "takeUpTo" prop_takeUpTo+ ,testProperty "group" prop_group+ ,testProperty "groupBy" prop_groupBy ,testProperty "convStream EOF" prop_convstream2 ,testProperty "convStream identity" prop_convstream ,testProperty "convStream identity 2" prop_convstream3