on master help sign in
Add isPrefixOf, isSubsequenceOf, stripPrefix to Prelude, StreamD.
dbcf536Pranay Sashank
benchmark/Linear.hs+5
@@ -194,6 +194,11 @@ main =
194 , benchIOSink "zipM" Ops.zipM
194 , benchIOSink "zipM" Ops.zipM
195 ]
195 ]
196 , benchIOSink "merge" Ops.merge
196 , benchIOSink "merge" Ops.merge
197+ , benchIOSink "isPrefixOf" Ops.isPrefixOf
198+ , benchIOSink "isPrefixAll" Ops.isPrefixAll
199+ , benchIOSink "isSubsequenceOf" Ops.isSubsequenceOf
200+ , benchIOSink "evenSubsequence" Ops.evenSubsequence
201+ , benchIOSink "stripPrefix" Ops.stripPrefix
197 , bgroup "mixedX4"
202 , bgroup "mixedX4"
198 [ benchIOSink "scan-map" (Ops.scanMap 4)
203 [ benchIOSink "scan-map" (Ops.scanMap 4)
199 , benchIOSink "drop-map" (Ops.dropMap 4)
204 , benchIOSink "drop-map" (Ops.dropMap 4)
benchmark/LinearOps.hs+33−1
@@ -324,6 +324,7 @@ insertSmall n = composeN n $ S.insertBy compare 0
324deleteSmall n = composeN n $ S.deleteBy (==) 0
324deleteSmall n = composeN n $ S.deleteBy (==) 0
325deleteLarge n = composeN n $ S.deleteBy (>=) maxValue
325deleteLarge n = composeN n $ S.deleteBy (>=) maxValue
326
326
327+
327-------------------------------------------------------------------------------
328-------------------------------------------------------------------------------
328-- Iteration
329-- Iteration
329-------------------------------------------------------------------------------
330-------------------------------------------------------------------------------
@@ -374,7 +375,14 @@ iterateDropWhileTrue = iterateSource (S.dropWhile (<= maxValue)) maxIters
374{-# INLINE zipM #-}
375{-# INLINE zipM #-}
375{-# INLINE merge #-}
376{-# INLINE merge #-}
376{-# INLINE concat #-}
377{-# INLINE concat #-}
378+{-# INLINE isPrefixOf #-}
379+{-# INLINE isPrefixAll #-}
380+{-# INLINE isSubsequenceOf #-}
381+{-# INLINE evenSubsequence #-}
382+{-# INLINE stripPrefix #-}
383+zip, zipM, concat, merge, isPrefixOf, isPrefixAll, isSubsequenceOf,
384+ evenSubsequence, stripPrefix
377−zip, zipM, concat, merge :: Monad m => Stream m Int -> m ()
385+ :: Monad m => Stream m Int -> m ()
378
386
379zip src = do
387zip src = do
380 r <- S.tail src
388 r <- S.tail src
@@ -390,6 +398,30 @@ merge src = do
390 let src1 = fromJust r
398 let src1 = fromJust r
391 transform (S.mergeBy P.compare src src1)
399 transform (S.mergeBy P.compare src src1)
392
400
401+isPrefixOf src = do
402+ let src1 = S.take 10 src
403+ _ <- S.isPrefixOf src1 src
404+ return ()
405+
406+isPrefixAll src = do
407+ _ <- S.isPrefixOf src src
408+ return ()
409+
410+isSubsequenceOf src = do
411+ let src1 = S.take 10 src
412+ _ <- S.isSubsequenceOf src1 src
413+ return ()
414+
415+evenSubsequence src = do
416+ let src1 = S.filter even src
417+ _ <- S.isSubsequenceOf src1 src
418+ return ()
419+
420+stripPrefix src = do
421+ let src1 = S.take 10 src
422+ _ <- S.stripPrefix src1 src
423+ return ()
424+
393{-# INLINE zipAsync #-}
425{-# INLINE zipAsync #-}
394{-# INLINE zipAsyncM #-}
426{-# INLINE zipAsyncM #-}
395zipAsync, zipAsyncM :: S.MonadAsync m => Stream m Int -> m ()
427zipAsync, zipAsyncM :: S.MonadAsync m => Stream m Int -> m ()
src/Streamly/Prelude.hs+32
@@ -125,6 +125,11 @@ module Streamly.Prelude
125 , last
125 , last
126 , init
126 , init
127
127
128+ -- Substreams
129+ , isPrefixOf
130+ , isSubsequenceOf
131+ , stripPrefix
132+
128 -- Conditional folds: may terminate early based on a condition
133 -- Conditional folds: may terminate early based on a condition
129 , null
134 , null
130 , elem
135 , elem
@@ -787,6 +792,33 @@ elemIndices a = findIndices (==a)
787elemIndex :: (Monad m, Eq a) => a -> SerialT m a -> m (Maybe Int)
792elemIndex :: (Monad m, Eq a) => a -> SerialT m a -> m (Maybe Int)
788elemIndex a = findIndex (==a)
793elemIndex a = findIndex (==a)
789
794
795+------------------------------------------------------------------------------
796+-- Substreams
797+------------------------------------------------------------------------------
798+
799+-- | Takes two streams and returns 'True' iff the first stream is a prefix
800+-- of the second.
801+--
802+{-# INLINE isPrefixOf #-}
803+isPrefixOf :: (Eq a, IsStream t, Monad m) => t m a -> t m a -> m Bool
804+isPrefixOf m1 m2 = S.isPrefixOf (toStreamD m1) (toStreamD m2)
805+
806+-- | Takes two streams and returns 'True' if all the elements of the
807+-- first stream occur, in order, in the second stream. The elements do
808+-- not have to occur consecutively.
809+--
810+{-# INLINE isSubsequenceOf #-}
811+isSubsequenceOf :: (Eq a, IsStream t, Monad m) => t m a -> t m a -> m Bool
812+isSubsequenceOf m1 m2 = S.isSubsequenceOf (toStreamD m1) (toStreamD m2)
813+
814+-- | Drops the given prefix from a stream. Returns Nothing if the stream
815+-- doesn't start with the prefix given.
816+--
817+{-# INLINE stripPrefix #-}
818+stripPrefix :: (Eq a, IsStream t, Monad m) => t m a -> t m a -> m (Maybe (t m a))
819+stripPrefix m1 m2 = fmap (fmap fromStreamD) $
820+ S.stripPrefix (toStreamD m1) (toStreamD m2)
821+
790------------------------------------------------------------------------------
822------------------------------------------------------------------------------
791-- Map and Fold
823-- Map and Fold
792------------------------------------------------------------------------------
824------------------------------------------------------------------------------
src/Streamly/Streams/StreamD.hs+72
@@ -90,6 +90,11 @@ module Streamly.Streams.StreamD
90 , find
90 , find
91 , (!!)
91 , (!!)
92
92
93+ -- ** Substreams
94+ , isPrefixOf
95+ , isSubsequenceOf
96+ , stripPrefix
97+
93 -- ** Map and Fold
98 -- ** Map and Fold
94 , mapM_
99 , mapM_
95
100
@@ -580,6 +585,73 @@ findIndices p (Stream step state) = Stream step' (state, 0)
580 Skip s -> Skip (s, i+1)
585 Skip s -> Skip (s, i+1)
581 Stop -> Stop
586 Stop -> Stop
582
587
588+------------------------------------------------------------------------------
589+-- Substreams
590+------------------------------------------------------------------------------
591+
592+{-# INLINE_NORMAL isPrefixOf #-}
593+isPrefixOf :: (Eq a, Monad m) => Stream m a -> Stream m a -> m Bool
594+isPrefixOf (Stream stepa ta) (Stream stepb tb) = go (ta, tb, Nothing)
595+ where
596+ go (sa, sb, Nothing) = do
597+ r <- stepa defState sa
598+ case r of
599+ Yield x sa' -> go (sa', sb, Just x)
600+ Skip sa' -> go (sa', sb, Nothing)
601+ Stop -> return True
602+
603+ go (sa, sb, Just x) = do
604+ r <- stepb defState sb
605+ case r of
606+ Yield y sb' ->
607+ if x == y
608+ then go (sa, sb', Nothing)
609+ else return False
610+ Skip sb' -> go (sa, sb', Just x)
611+ Stop -> return False
612+
613+{-# INLINE_NORMAL isSubsequenceOf #-}
614+isSubsequenceOf :: (Eq a, Monad m) => Stream m a -> Stream m a -> m Bool
615+isSubsequenceOf (Stream stepa ta) (Stream stepb tb) = go (ta, tb, Nothing)
616+ where
617+ go (sa, sb, Nothing) = do
618+ r <- stepa defState sa
619+ case r of
620+ Yield x sa' -> go (sa', sb, Just x)
621+ Skip sa' -> go (sa', sb, Nothing)
622+ Stop -> return True
623+
624+ go (sa, sb, Just x) = do
625+ r <- stepb defState sb
626+ case r of
627+ Yield y sb' ->
628+ if x == y
629+ then go (sa, sb', Nothing)
630+ else go (sa, sb', Just x)
631+ Skip sb' -> go (sa, sb', Just x)
632+ Stop -> return False
633+
634+{-# INLINE_NORMAL stripPrefix #-}
635+stripPrefix :: (Eq a, Monad m) => Stream m a -> Stream m a -> m (Maybe (Stream m a))
636+stripPrefix (Stream stepa ta) (Stream stepb tb) = go (ta, tb, Nothing)
637+ where
638+ go (sa, sb, Nothing) = do
639+ r <- stepa defState sa
640+ case r of
641+ Yield x sa' -> go (sa', sb, Just x)
642+ Skip sa' -> go (sa', sb, Nothing)
643+ Stop -> return $ Just (Stream stepb sb)
644+
645+ go (sa, sb, Just x) = do
646+ r <- stepb defState sb
647+ case r of
648+ Yield y sb' ->
649+ if x == y
650+ then go (sa, sb', Nothing)
651+ else return Nothing
652+ Skip sb' -> go (sa, sb', Just x)
653+ Stop -> return Nothing
654+
583------------------------------------------------------------------------------
655------------------------------------------------------------------------------
584-- Map and Fold
656-- Map and Fold
585------------------------------------------------------------------------------
657------------------------------------------------------------------------------
test/Prop.hs+14−3
@@ -14,7 +14,8 @@ import Data.IORef (readIORef, modifyIORef, newIORef)
14import Data.List
14import Data.List
15 (sort, foldl', scanl', findIndices, findIndex, elemIndices,
15 (sort, foldl', scanl', findIndices, findIndex, elemIndices,
16 elemIndex, find, insertBy, intersperse, foldl1', (\\),
16 elemIndex, find, insertBy, intersperse, foldl1', (\\),
17− maximumBy, minimumBy, deleteBy)
17+ maximumBy, minimumBy, deleteBy, isPrefixOf, isSubsequenceOf,
18+ stripPrefix)
18import Data.Maybe (mapMaybe)
19import Data.Maybe (mapMaybe)
19import GHC.Word (Word8)
20import GHC.Word (Word8)
20
21
@@ -584,14 +585,24 @@ eliminationOps constr desc t = do
584 prop (desc <> " findIndex") $ eliminateOp constr (findIndex odd) $ S.findIndex odd . t
585 prop (desc <> " findIndex") $ eliminateOp constr (findIndex odd) $ S.findIndex odd . t
585 prop (desc <> " elemIndex") $ eliminateOp constr (elemIndex 3) $ S.elemIndex 3 . t
586 prop (desc <> " elemIndex") $ eliminateOp constr (elemIndex 3) $ S.elemIndex 3 . t
586
587
587− prop (desc <> " !!") $ eliminateOp constr (wrapOutOfBounds (!!) 5) $ (S.!! 5) . t
588+ prop (desc <> " !! 5") $ eliminateOp constr (wrapOutOfBounds (!!) 5) $ (S.!! 5) . t
588− prop (desc <> " !!") $ eliminateOp constr (wrapOutOfBounds (!!) 0) $ (S.!! 0) . t
589+ prop (desc <> " !! 4") $ eliminateOp constr (wrapOutOfBounds (!!) 0) $ (S.!! 0) . t
589
590
590 prop (desc <> " find") $ eliminateOp constr (find even) $ S.find even . t
591 prop (desc <> " find") $ eliminateOp constr (find even) $ S.find even . t
591 prop (desc <> " lookup") $
592 prop (desc <> " lookup") $
592 eliminateOp constr (lookup 3 . flip zip [1..]) $
593 eliminateOp constr (lookup 3 . flip zip [1..]) $
593 S.lookup 3 . S.zipWith (\a b -> (b, a)) (S.fromList [(1::Int)..]) . t
594 S.lookup 3 . S.zipWith (\a b -> (b, a)) (S.fromList [(1::Int)..]) . t
594
595
596+ -- XXX Write better tests for substreams.
597+ prop (desc <> " isPrefixOf 10") $ eliminateOp constr (isPrefixOf [1..10]) $
598+ S.isPrefixOf (S.fromList [(1::Int)..10]) . t
599+ prop (desc <> " isSubsequenceOf 10") $
600+ eliminateOp constr (isSubsequenceOf $ filter even [1..10]) $
601+ S.isSubsequenceOf (S.fromList $ filter even [(1::Int)..10]) . t
602+ prop (desc <> " stripPrefix 10") $ eliminateOp constr (stripPrefix [1..10]) $
603+ (\s -> s >>= maybe (return Nothing) (fmap Just . S.toList)) .
604+ S.stripPrefix (S.fromList [(1::Int)..10]) . t
605+
595 prop (desc <> " the") $ eliminateOp constr wrapThe $ S.the . t
606 prop (desc <> " the") $ eliminateOp constr wrapThe $ S.the . t
596
607
597-- head/tail/last may depend on the order in case of parallel streams
608-- head/tail/last may depend on the order in case of parallel streams