on master help sign in
Add isPrefixOf, isSubsequenceOf, stripPrefix to Prelude, StreamD.
commitdbcf5363e8232dca980d4e7376fb6d1f205a9f86 authorPranay Sashank <pranaysashank@gmail.com> dateThu 15 Nov 2018, 21:35 (8 years ago) parent55e582e
5 files changed+156−4
benchmark/Linear.hs+5 benchmark/LinearOps.hs+33−1 src/Streamly/Prelude.hs+32 src/Streamly/Streams/StreamD.hs+72 test/Prop.hs+14−3
benchmark/Linear.hs+5
@@ -194,6 +194,11 @@ main =
194194 , benchIOSink "zipM" Ops.zipM
195195 ]
196196 , 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
197202 , bgroup "mixedX4"
198203 [ benchIOSink "scan-map" (Ops.scanMap 4)
199204 , benchIOSink "drop-map" (Ops.dropMap 4)
benchmark/LinearOps.hs+33−1
@@ -324,6 +324,7 @@ insertSmall n = composeN n $ S.insertBy compare 0
324324deleteSmall n = composeN n $ S.deleteBy (==) 0
325325deleteLarge n = composeN n $ S.deleteBy (>=) maxValue
326326
327+
327328-------------------------------------------------------------------------------
328329-- Iteration
329330-------------------------------------------------------------------------------
@@ -374,7 +375,14 @@ iterateDropWhileTrue = iterateSource (S.dropWhile (<= maxValue)) maxIters
374375{-# INLINE zipM #-}
375376{-# INLINE merge #-}
376377{-# INLINE concat #-}
377−zip, zipM, concat, merge :: Monad m => Stream m Int -> m ()
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
385+ :: Monad m => Stream m Int -> m ()
378386
379387zip src = do
380388 r <- S.tail src
@@ -390,6 +398,30 @@ merge src = do
390398 let src1 = fromJust r
391399 transform (S.mergeBy P.compare src src1)
392400
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+
393425{-# INLINE zipAsync #-}
394426{-# INLINE zipAsyncM #-}
395427zipAsync, zipAsyncM :: S.MonadAsync m => Stream m Int -> m ()
src/Streamly/Prelude.hs+32
@@ -125,6 +125,11 @@ module Streamly.Prelude
125125 , last
126126 , init
127127
128+ -- Substreams
129+ , isPrefixOf
130+ , isSubsequenceOf
131+ , stripPrefix
132+
128133 -- Conditional folds: may terminate early based on a condition
129134 , null
130135 , elem
@@ -787,6 +792,33 @@ elemIndices a = findIndices (==a)
787792elemIndex :: (Monad m, Eq a) => a -> SerialT m a -> m (Maybe Int)
788793elemIndex a = findIndex (==a)
789794
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+
790822------------------------------------------------------------------------------
791823-- Map and Fold
792824------------------------------------------------------------------------------
src/Streamly/Streams/StreamD.hs+72
@@ -90,6 +90,11 @@ module Streamly.Streams.StreamD
9090 , find
9191 , (!!)
9292
93+ -- ** Substreams
94+ , isPrefixOf
95+ , isSubsequenceOf
96+ , stripPrefix
97+
9398 -- ** Map and Fold
9499 , mapM_
95100
@@ -580,6 +585,73 @@ findIndices p (Stream step state) = Stream step' (state, 0)
580585 Skip s -> Skip (s, i+1)
581586 Stop -> Stop
582587
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+
583655------------------------------------------------------------------------------
584656-- Map and Fold
585657------------------------------------------------------------------------------
test/Prop.hs+14−3
@@ -14,7 +14,8 @@ import Data.IORef (readIORef, modifyIORef, newIORef)
1414import Data.List
1515 (sort, foldl', scanl', findIndices, findIndex, elemIndices,
1616 elemIndex, find, insertBy, intersperse, foldl1', (\\),
17− maximumBy, minimumBy, deleteBy)
17+ maximumBy, minimumBy, deleteBy, isPrefixOf, isSubsequenceOf,
18+ stripPrefix)
1819import Data.Maybe (mapMaybe)
1920import GHC.Word (Word8)
2021
@@ -584,14 +585,24 @@ eliminationOps constr desc t = do
584585 prop (desc <> " findIndex") $ eliminateOp constr (findIndex odd) $ S.findIndex odd . t
585586 prop (desc <> " elemIndex") $ eliminateOp constr (elemIndex 3) $ S.elemIndex 3 . t
586587
587− prop (desc <> " !!") $ eliminateOp constr (wrapOutOfBounds (!!) 5) $ (S.!! 5) . t
588− prop (desc <> " !!") $ eliminateOp constr (wrapOutOfBounds (!!) 0) $ (S.!! 0) . t
588+ prop (desc <> " !! 5") $ eliminateOp constr (wrapOutOfBounds (!!) 5) $ (S.!! 5) . t
589+ prop (desc <> " !! 4") $ eliminateOp constr (wrapOutOfBounds (!!) 0) $ (S.!! 0) . t
589590
590591 prop (desc <> " find") $ eliminateOp constr (find even) $ S.find even . t
591592 prop (desc <> " lookup") $
592593 eliminateOp constr (lookup 3 . flip zip [1..]) $
593594 S.lookup 3 . S.zipWith (\a b -> (b, a)) (S.fromList [(1::Int)..]) . t
594595
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+
595606 prop (desc <> " the") $ eliminateOp constr wrapThe $ S.the . t
596607
597608-- head/tail/last may depend on the order in case of parallel streams