Skip to content

Commit 721e235

Browse files
TikhonJelvistreeowl
authored andcommitted
Implement merge, mergeOn and mergeBy in Streaming.Prelude. (#53)
These functions provide an efficient way to merged ordered streams of elements while preserving their order. The merge/mergeOn/mergeBy pattern is inspired by sort/sortOn/sortBy from Data.List. The actual implementation of mergeBy is closely based on zipWith and should have roughly the same performance characteristics.
1 parent 356865d commit 721e235

1 file changed

Lines changed: 74 additions & 0 deletions

File tree

src/Streaming/Prelude.hs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,13 @@ module Streaming.Prelude (
225225
, partitionEithers
226226
, partition
227227

228+
-- * Merging streams
229+
-- $merging
230+
231+
, merge
232+
, mergeOn
233+
, mergeBy
234+
228235
-- * Maybes
229236
-- $maybes
230237
, catMaybes
@@ -276,6 +283,7 @@ import Data.Functor.Compose
276283
import Data.Functor.Of
277284
import qualified Data.Set as Set
278285
import qualified Data.IntSet as IntSet
286+
import Data.Ord (Ordering (..), comparing)
279287

280288
-- instance (Eq a) => Eq1 (Of a) where eq1 = (==)
281289
-- instance (Ord a) => Ord1 (Of a) where compare1 = compare
@@ -2719,6 +2727,72 @@ unzip = loop where
27192727

27202728

27212729

2730+
{- $merging
2731+
These functions combine two sorted streams of orderable elements
2732+
into one sorted stream. The elements of the merged stream are
2733+
guaranteed to be in a sorted order if the two input streams are
2734+
also sorted.
2735+
2736+
The merge operation is /left-biased/: when merging two elements
2737+
that compare as equal, the left element is chosen first.
2738+
-}
2739+
2740+
{- | Merge two streams of elements ordered with their 'Ord' instance.
2741+
2742+
The return values of both streams are returned.
2743+
2744+
>>> S.print $ merge (each [1,3,5]) (each [2,4])
2745+
1
2746+
2
2747+
3
2748+
4
2749+
5
2750+
((), ())
2751+
2752+
-}
2753+
merge :: (Monad m, Ord a)
2754+
=> Stream (Of a) m r
2755+
-> Stream (Of a) m s
2756+
-> Stream (Of a) m (r, s)
2757+
merge = mergeBy compare
2758+
{-# INLINE merge #-}
2759+
2760+
{- | Merge two streams, ordering them by applying the given function to
2761+
each element before comparing.
2762+
2763+
The return values of both streams are returned.
2764+
-}
2765+
mergeOn :: (Monad m, Ord b)
2766+
=> (a -> b)
2767+
-> Stream (Of a) m r
2768+
-> Stream (Of a) m s
2769+
-> Stream (Of a) m (r, s)
2770+
mergeOn f = mergeBy (comparing f)
2771+
{-# INLINE mergeOn #-}
2772+
2773+
{- | Merge two streams, ordering the elements using the given comparison function.
2774+
2775+
The return values of both streams are returned.
2776+
-}
2777+
mergeBy :: Monad m
2778+
=> (a -> a -> Ordering)
2779+
-> Stream (Of a) m r
2780+
-> Stream (Of a) m s
2781+
-> Stream (Of a) m (r, s)
2782+
mergeBy cmp = loop
2783+
where
2784+
loop str0 str1 = case str0 of
2785+
Return r0 -> (\ r1 -> (r0, r1)) <$> str1
2786+
Effect m -> Effect $ fmap (\ str -> loop str str1) m
2787+
Step (a :> rest0) -> case str1 of
2788+
Return r1 -> (\ r0 -> (r0, r1)) <$> str0
2789+
Effect m -> Effect $ fmap (loop str0) m
2790+
Step (b :> rest1) -> case cmp a b of
2791+
LT -> Step (a :> loop rest0 str1)
2792+
EQ -> Step (a :> loop rest0 str1) -- left-biased
2793+
GT -> Step (b :> loop str0 rest1)
2794+
{-# INLINABLE mergeBy #-}
2795+
27222796
{- $maybes
27232797
These functions discard the 'Nothing's that they encounter. They are analogous
27242798
to the functions from @Data.Maybe@ that share their names.

0 commit comments

Comments
 (0)