Using Haskell-streaming, I can easily group a stream and take sum on each group.
>>> S.print $ mapped S.toList $ S.groupBy (\ x y -> x*y>0) $ each [-1,-2,3,4,5,-6]
[-1,-2]
[3,4,5]
[-6]
>>> S.print $S.map sum $ mapped S.toList $ S.groupBy (\ x y -> x*y>0) $ each [-1,-2,3,4,5,-6]
-3
12
-6
How to have a function myfn that generates a stream that is a merge of the two above in an order sensitive way? I.e. I wish to have a result stream of
>>> myfn $ each [-1,-2,3,4,5,-6]
-1:> -2:> -3:> 3:> 4:> 5:> 12:> -6:> -6:> ()
The solution involves making the function argument of
mappedboth accumulate the list and calculate the sum, in one pass.That can be done with
storeI think, but I find the streaming sinks from foldl easier to use. TheirApplicativeinstance lets us build compositeFolds from simpler ones:Where
L.purely,L.listandL.sumare from "foldl".The finishing touch is taking each pair
([Int],Int)coming out ofmappedand replacing it with a substream usingfor.Putting it to work:
Edit: Come to think of it, the previous solution is flawed. We are only interested in a streamed result, yet we accumulate each individual group in memory using
S.toListorL.listbefore sending it downstream. But what if one group happens to be bigger than the available memory in the machine?Here's a solution that streams perfectly and is indifferent to the size of each group:
What has changed? First, we use
mapsinstead ofmapped, because now we want to transform the subgroup streams, instead of returning a result in the base monad.For each subgroup stream, we use
storeto perform a summing fold without destroying the stream. Then we take the result of the fold and append it back to the stream, while also taking care of preserving the original return value as required bymaps.The only step left is to rejoin the subgroups using
concats.