on master help sign in
Implement Data.Parser.ParserD.groupBy

- Modify Data.Parser.groupBy accordingly - Add tests for both - Add benchmarks for both

commitb6ce6a3624932596e37016ffbadc79453e15959c authorAdithya Kumar <adithya@composewell.com> dateThu 10 Dec 2020, 12:20 (6 years ago) parentac3af87
6 files changed+82−17
benchmark/Streamly/Benchmark/Data/Parser.hs+5 benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+5 src/Streamly/Internal/Data/Parser.hs+2−4 src/Streamly/Internal/Data/Parser/ParserD.hs+28−7 test/Streamly/Test/Data/Parser.hs+21−3 test/Streamly/Test/Data/Parser/ParserD.hs+21−3
benchmark/Streamly/Benchmark/Data/Parser.hs+5
@@ -72,6 +72,10 @@ drainWhile value = IP.parse (PR.drainWhile (<= value))
7272takeWhile :: MonadCatch m => Int -> SerialT m Int -> m ()
7373takeWhile value = IP.parse (PR.takeWhile (<= value) FL.drain)
7474
75+{-# INLINE groupBy #-}
76+groupBy :: MonadCatch m => SerialT m Int -> m ()
77+groupBy = IP.parse (PR.groupBy (<=) FL.drain)
78+
7579{-# INLINE many #-}
7680many :: MonadCatch m => SerialT m Int -> m Int
7781many = IP.parse (PR.many FL.length (PR.satisfy (> 0)))
@@ -258,6 +262,7 @@ o_1_space_serial value =
258262 [ benchIOSink value "takeEQ" $ takeEQ value
259263 , benchIOSink value "takeWhile" $ takeWhile value
260264 , benchIOSink value "drainWhile" $ drainWhile value
265+ , benchIOSink value "groupBy" $ groupBy
261266 , benchIOSink value "splitAp" $ splitAp value
262267 , benchIOSink value "splitApBefore" $ splitApBefore value
263268 , benchIOSink value "splitApAfter" $ splitApAfter value
benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+5
@@ -67,6 +67,10 @@ drainWhile p = PR.takeWhile p FL.drain
6767takeWhile :: MonadThrow m => Int -> SerialT m Int -> m ()
6868takeWhile value = IP.parseD (drainWhile (<= value))
6969
70+{-# INLINE groupBy #-}
71+groupBy :: MonadThrow m => SerialT m Int -> m ()
72+groupBy = IP.parseD (PR.groupBy (<=) FL.drain)
73+
7074{-# INLINE many #-}
7175many :: MonadCatch m => SerialT m Int -> m Int
7276many = IP.parseD (PR.many FL.length (PR.satisfy (> 0)))
@@ -195,6 +199,7 @@ moduleName = "Data.Parser.ParserD"
195199o_1_space_serial :: Int -> [Benchmark]
196200o_1_space_serial value =
197201 [ benchIOSink value "takeWhile" $ takeWhile value
202+ , benchIOSink value "groupBy" $ groupBy
198203 , benchIOSink value "split (all,any)" $ splitAllAny value
199204 , benchIOSink value "many" many
200205 , benchIOSink value "some" some
src/Streamly/Internal/Data/Parser.hs+2−4
@@ -573,10 +573,8 @@ wordBy = undefined
573573-- /Unimplemented/
574574--
575575{-# INLINABLE groupBy #-}
576−groupBy ::
577− -- Monad m =>
578− (a -> a -> Bool) -> Fold m a b -> Parser m a b
579−groupBy = undefined
576+groupBy :: MonadCatch m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
577+groupBy cmp = K.toParserK . D.groupBy cmp
580578
581579-- | Match the given sequence of elements using the given comparison function.
582580--
src/Streamly/Internal/Data/Parser/ParserD.hs+28−7
@@ -457,15 +457,36 @@ wordBy ::
457457 (a -> Bool) -> Fold m a b -> Parser m a b
458458wordBy = undefined
459459
460+data GroupByState a s
461+ = GroupByInit !s
462+ | GroupByGrouping !a !s
463+
460464-- | See 'Streamly.Internal.Data.Parser.groupBy'.
461465--
462−-- /Unimplemented/
463−--
464−{-# INLINABLE groupBy #-}
465−groupBy ::
466− -- Monad m =>
467− (a -> a -> Bool) -> Fold m a b -> Parser m a b
468−groupBy = undefined
466+{-# INLINE groupBy #-}
467+groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
468+groupBy cmp (Fold fstep finitial fextract) = Parser step initial extract
469+
470+ where
471+
472+ {-# INLINE grouper #-}
473+ grouper s a0 a = do
474+ res <- fstep s a
475+ return
476+ $ case res of
477+ FL.Done b -> Done 0 b
478+ FL.Partial s1 -> Partial 0 (GroupByGrouping a0 s1)
479+
480+ initial = GroupByInit <$> finitial
481+
482+ step (GroupByInit s) a = grouper s a a
483+ step (GroupByGrouping a0 s) a =
484+ if cmp a0 a
485+ then grouper s a0 a
486+ else Done 1 <$> fextract s
487+
488+ extract (GroupByInit s) = fextract s
489+ extract (GroupByGrouping _ s) = fextract s
469490
470491-- XXX use an Unfold instead of a list?
471492-- XXX custom combinators for matching list, array and stream?
test/Streamly/Test/Data/Parser.hs+21−3
@@ -12,12 +12,13 @@ import Test.QuickCheck.Monadic (monadicIO, assert, run)
1212
1313import Prelude hiding (sequence)
1414
15+import qualified Data.List as List
16+import qualified Prelude
17+import qualified Streamly.Internal.Data.Array.Storable.Foreign as A
18+import qualified Streamly.Internal.Data.Fold as FL
1519import qualified Streamly.Internal.Data.Parser as P
1620import qualified Streamly.Internal.Data.Stream.IsStream as S
17−import qualified Streamly.Internal.Data.Fold as FL
18−import qualified Streamly.Internal.Data.Array.Storable.Foreign as A
1921import qualified Test.Hspec as H
20−import qualified Prelude
2122
2223#if MIN_VERSION_QuickCheck(2,14,0)
2324
@@ -300,6 +301,22 @@ takeWhile1 =
300301 where
301302 predicate = (== 0)
302303
304+groupBy :: Property
305+groupBy =
306+ forAll (listOf (chooseInt (0, 1)))
307+ $ \ls ->
308+ case S.parse parser (S.fromList ls) of
309+ Right parsed -> checkListEqual parsed (groupByLF ls)
310+ Left _ -> property False
311+
312+ where
313+
314+ cmp = (==)
315+ parser = P.groupBy cmp FL.toList
316+ groupByLF lst
317+ | null lst = []
318+ | otherwise = head $ List.groupBy cmp lst
319+
303320-- splitWithPass :: Property
304321-- splitWithPass =
305322-- forAll (listOf (chooseInt (0, 1))) $ \ls ->
@@ -609,6 +626,7 @@ main =
609626 -- prop "lookAhead . take n >> lookAhead . take n = lookAhead . take n, else fail" lookAhead
610627 prop "P.takeWhile = Prelude.takeWhile" Main.takeWhile
611628 prop "P.takeWhile = Prelude.takeWhile if taken something, else check why failed" takeWhile1
629+ prop "P.groupBy = Prelude.head . Prelude.groupBy" groupBy
612630 -- prop "" splitWithPass
613631 -- prop "" splitWithFailLeft
614632 -- prop "" splitWithFailRight
test/Streamly/Test/Data/Parser/ParserD.hs+21−3
@@ -10,11 +10,12 @@ import Test.QuickCheck
1010 property, listOf, vectorOf, (.&&.), Gen, suchThat)
1111import Test.QuickCheck.Monadic (monadicIO, assert, run)
1212
13+import qualified Data.List as List
14+import qualified Prelude
15+import qualified Streamly.Internal.Data.Array.Storable.Foreign as A
16+import qualified Streamly.Internal.Data.Fold as FL
1317import qualified Streamly.Internal.Data.Parser.ParserD as P
1418import qualified Streamly.Internal.Data.Stream.IsStream as S
15−import qualified Streamly.Internal.Data.Fold as FL
16−import qualified Streamly.Internal.Data.Array.Storable.Foreign as A
17−import qualified Prelude
1819import qualified Test.Hspec as H
1920
2021import Prelude hiding (sequence)
@@ -287,6 +288,22 @@ takeWhile1 =
287288 where
288289 predicate = (== 0)
289290
291+groupBy :: Property
292+groupBy =
293+ forAll (listOf (chooseInt (0, 1)))
294+ $ \ls ->
295+ case S.parseD parser (S.fromList ls) of
296+ Right parsed -> checkListEqual parsed (groupByLF ls)
297+ Left _ -> property False
298+
299+ where
300+
301+ cmp = (==)
302+ parser = P.groupBy cmp FL.toList
303+ groupByLF lst
304+ | null lst = []
305+ | otherwise = head $ List.groupBy cmp lst
306+
290307sliceSepBy :: Property
291308sliceSepBy =
292309 forAll (listOf (chooseInt (0, 1))) $ \ls ->
@@ -637,6 +654,7 @@ main =
637654 prop "lookAhead . take n >> lookAhead . take n = lookAhead . take n, else fail" lookAhead
638655 prop "P.takeWhile = Prelude.takeWhile" Main.takeWhile
639656 prop "P.takeWhile1 = Prelude.takeWhile if taken something, else check why failed" takeWhile1
657+ prop "P.groupBy = Prelude.head . Prelude.groupBy" groupBy
640658 prop "P.sliceSepBy = Prelude.takeWhile (not . predicate)" sliceSepBy
641659 prop "P.sliceSepByMax = Prelude.take n (Prelude.takeWhile (not . predicate)" sliceSepByMax
642660 prop "parse 0, then 1, else fail" splitWith