on master help sign in
Implement Data.Parser.ParserD.groupBy
b6ce6a3Adithya Kumar
benchmark/Streamly/Benchmark/Data/Parser.hs+5
@@ -72,6 +72,10 @@ drainWhile value = IP.parse (PR.drainWhile (<= value))
72takeWhile :: MonadCatch m => Int -> SerialT m Int -> m ()
72takeWhile :: MonadCatch m => Int -> SerialT m Int -> m ()
73takeWhile value = IP.parse (PR.takeWhile (<= value) FL.drain)
73takeWhile value = IP.parse (PR.takeWhile (<= value) FL.drain)
74
74
75+{-# INLINE groupBy #-}
76+groupBy :: MonadCatch m => SerialT m Int -> m ()
77+groupBy = IP.parse (PR.groupBy (<=) FL.drain)
78+
75{-# INLINE many #-}
79{-# INLINE many #-}
76many :: MonadCatch m => SerialT m Int -> m Int
80many :: MonadCatch m => SerialT m Int -> m Int
77many = IP.parse (PR.many FL.length (PR.satisfy (> 0)))
81many = IP.parse (PR.many FL.length (PR.satisfy (> 0)))
@@ -258,6 +262,7 @@ o_1_space_serial value =
258 [ benchIOSink value "takeEQ" $ takeEQ value
262 [ benchIOSink value "takeEQ" $ takeEQ value
259 , benchIOSink value "takeWhile" $ takeWhile value
263 , benchIOSink value "takeWhile" $ takeWhile value
260 , benchIOSink value "drainWhile" $ drainWhile value
264 , benchIOSink value "drainWhile" $ drainWhile value
265+ , benchIOSink value "groupBy" $ groupBy
261 , benchIOSink value "splitAp" $ splitAp value
266 , benchIOSink value "splitAp" $ splitAp value
262 , benchIOSink value "splitApBefore" $ splitApBefore value
267 , benchIOSink value "splitApBefore" $ splitApBefore value
263 , benchIOSink value "splitApAfter" $ splitApAfter value
268 , benchIOSink value "splitApAfter" $ splitApAfter value
benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+5
@@ -67,6 +67,10 @@ drainWhile p = PR.takeWhile p FL.drain
67takeWhile :: MonadThrow m => Int -> SerialT m Int -> m ()
67takeWhile :: MonadThrow m => Int -> SerialT m Int -> m ()
68takeWhile value = IP.parseD (drainWhile (<= value))
68takeWhile value = IP.parseD (drainWhile (<= value))
69
69
70+{-# INLINE groupBy #-}
71+groupBy :: MonadThrow m => SerialT m Int -> m ()
72+groupBy = IP.parseD (PR.groupBy (<=) FL.drain)
73+
70{-# INLINE many #-}
74{-# INLINE many #-}
71many :: MonadCatch m => SerialT m Int -> m Int
75many :: MonadCatch m => SerialT m Int -> m Int
72many = IP.parseD (PR.many FL.length (PR.satisfy (> 0)))
76many = IP.parseD (PR.many FL.length (PR.satisfy (> 0)))
@@ -195,6 +199,7 @@ moduleName = "Data.Parser.ParserD"
195o_1_space_serial :: Int -> [Benchmark]
199o_1_space_serial :: Int -> [Benchmark]
196o_1_space_serial value =
200o_1_space_serial value =
197 [ benchIOSink value "takeWhile" $ takeWhile value
201 [ benchIOSink value "takeWhile" $ takeWhile value
202+ , benchIOSink value "groupBy" $ groupBy
198 , benchIOSink value "split (all,any)" $ splitAllAny value
203 , benchIOSink value "split (all,any)" $ splitAllAny value
199 , benchIOSink value "many" many
204 , benchIOSink value "many" many
200 , benchIOSink value "some" some
205 , benchIOSink value "some" some
src/Streamly/Internal/Data/Parser.hs+2−4
@@ -573,10 +573,8 @@ wordBy = undefined
573-- /Unimplemented/
573-- /Unimplemented/
574--
574--
575{-# INLINABLE groupBy #-}
575{-# INLINABLE groupBy #-}
576−groupBy ::
577− -- Monad m =>
578− (a -> a -> Bool) -> Fold m a b -> Parser m a b
576+groupBy :: MonadCatch m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
579−groupBy = undefined
577+groupBy cmp = K.toParserK . D.groupBy cmp
580
578
581-- | Match the given sequence of elements using the given comparison function.
579-- | Match the given sequence of elements using the given comparison function.
582--
580--
src/Streamly/Internal/Data/Parser/ParserD.hs+28−7
@@ -457,15 +457,36 @@ wordBy ::
457 (a -> Bool) -> Fold m a b -> Parser m a b
457 (a -> Bool) -> Fold m a b -> Parser m a b
458wordBy = undefined
458wordBy = undefined
459
459
460+data GroupByState a s
461+ = GroupByInit !s
462+ | GroupByGrouping !a !s
463+
460-- | See 'Streamly.Internal.Data.Parser.groupBy'.
464-- | See 'Streamly.Internal.Data.Parser.groupBy'.
461--
465--
462−-- /Unimplemented/
463−--
464−{-# INLINABLE groupBy #-}
466+{-# INLINE groupBy #-}
465−groupBy ::
466− -- Monad m =>
467− (a -> a -> Bool) -> Fold m a b -> Parser m a b
467+groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser m a b
468−groupBy = undefined
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
469
490
470-- XXX use an Unfold instead of a list?
491-- XXX use an Unfold instead of a list?
471-- XXX custom combinators for matching list, array and stream?
492-- 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)
12
12
13import Prelude hiding (sequence)
13import Prelude hiding (sequence)
14
14
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
15import qualified Streamly.Internal.Data.Parser as P
19import qualified Streamly.Internal.Data.Parser as P
16import qualified Streamly.Internal.Data.Stream.IsStream as S
20import 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
19import qualified Test.Hspec as H
21import qualified Test.Hspec as H
20−import qualified Prelude
21
22
22#if MIN_VERSION_QuickCheck(2,14,0)
23#if MIN_VERSION_QuickCheck(2,14,0)
23
24
@@ -300,6 +301,22 @@ takeWhile1 =
300 where
301 where
301 predicate = (== 0)
302 predicate = (== 0)
302
303
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+
303-- splitWithPass :: Property
320-- splitWithPass :: Property
304-- splitWithPass =
321-- splitWithPass =
305-- forAll (listOf (chooseInt (0, 1))) $ \ls ->
322-- forAll (listOf (chooseInt (0, 1))) $ \ls ->
@@ -609,6 +626,7 @@ main =
609 -- prop "lookAhead . take n >> lookAhead . take n = lookAhead . take n, else fail" lookAhead
626 -- prop "lookAhead . take n >> lookAhead . take n = lookAhead . take n, else fail" lookAhead
610 prop "P.takeWhile = Prelude.takeWhile" Main.takeWhile
627 prop "P.takeWhile = Prelude.takeWhile" Main.takeWhile
611 prop "P.takeWhile = Prelude.takeWhile if taken something, else check why failed" takeWhile1
628 prop "P.takeWhile = Prelude.takeWhile if taken something, else check why failed" takeWhile1
629+ prop "P.groupBy = Prelude.head . Prelude.groupBy" groupBy
612 -- prop "" splitWithPass
630 -- prop "" splitWithPass
613 -- prop "" splitWithFailLeft
631 -- prop "" splitWithFailLeft
614 -- prop "" splitWithFailRight
632 -- prop "" splitWithFailRight
test/Streamly/Test/Data/Parser/ParserD.hs+21−3
@@ -10,11 +10,12 @@ import Test.QuickCheck
10 property, listOf, vectorOf, (.&&.), Gen, suchThat)
10 property, listOf, vectorOf, (.&&.), Gen, suchThat)
11import Test.QuickCheck.Monadic (monadicIO, assert, run)
11import Test.QuickCheck.Monadic (monadicIO, assert, run)
12
12
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
13import qualified Streamly.Internal.Data.Parser.ParserD as P
17import qualified Streamly.Internal.Data.Parser.ParserD as P
14import qualified Streamly.Internal.Data.Stream.IsStream as S
18import 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
18import qualified Test.Hspec as H
19import qualified Test.Hspec as H
19
20
20import Prelude hiding (sequence)
21import Prelude hiding (sequence)
@@ -287,6 +288,22 @@ takeWhile1 =
287 where
288 where
288 predicate = (== 0)
289 predicate = (== 0)
289
290
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+
290sliceSepBy :: Property
307sliceSepBy :: Property
291sliceSepBy =
308sliceSepBy =
292 forAll (listOf (chooseInt (0, 1))) $ \ls ->
309 forAll (listOf (chooseInt (0, 1))) $ \ls ->
@@ -637,6 +654,7 @@ main =
637 prop "lookAhead . take n >> lookAhead . take n = lookAhead . take n, else fail" lookAhead
654 prop "lookAhead . take n >> lookAhead . take n = lookAhead . take n, else fail" lookAhead
638 prop "P.takeWhile = Prelude.takeWhile" Main.takeWhile
655 prop "P.takeWhile = Prelude.takeWhile" Main.takeWhile
639 prop "P.takeWhile1 = Prelude.takeWhile if taken something, else check why failed" takeWhile1
656 prop "P.takeWhile1 = Prelude.takeWhile if taken something, else check why failed" takeWhile1
657+ prop "P.groupBy = Prelude.head . Prelude.groupBy" groupBy
640 prop "P.sliceSepBy = Prelude.takeWhile (not . predicate)" sliceSepBy
658 prop "P.sliceSepBy = Prelude.takeWhile (not . predicate)" sliceSepBy
641 prop "P.sliceSepByMax = Prelude.take n (Prelude.takeWhile (not . predicate)" sliceSepByMax
659 prop "P.sliceSepByMax = Prelude.take n (Prelude.takeWhile (not . predicate)" sliceSepByMax
642 prop "parse 0, then 1, else fail" splitWith
660 prop "parse 0, then 1, else fail" splitWith