![](/img/trans.png)
[英]Haskell streaming - how to separate 1 stream into 2 after copy?
[英]Haskell streaming - how to merge original stream with result stream
使用Haskell-streaming ,我可以輕松地對流進行分組並對每個組求和。
>>> 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
如何讓函數myfn
生成一個以順序敏感的方式合並上述兩者的流? 即我希望有一個結果流
>>> myfn $ each [-1,-2,3,4,5,-6]
-1:> -2:> -3:> 3:> 4:> 5:> 12:> -6:> -6:> ()
該解決方案涉及使mapped
的函數參數在一次傳遞中既累加列表又計算總和。
我認為這可以通過store
來完成,但我發現foldl 中的流接收器更易於使用。 他們的Applicative
實例讓我們可以從更簡單的實例構建復合Fold
:
foo :: Monad m
=> (Int -> Int -> Bool)
-> Stream (Of Int) m ()
-> Stream (Of Int) m ()
foo p =
flip S.for (\(xs,total) -> S.each xs *> S.yield total)
. mapped (L.purely S.fold $ (,) <$> L.list <*> L.sum)
. S.groupBy p
其中L.purely
、 L.list
和L.sum
來自“foldl”。
畫龍點睛的是從mapped
取出每一對([Int],Int)
並將其替換為使用for
的子流。
讓它工作:
*Main> S.print $ foo (\x y -> x*y>0) $ S.each [-1,-2,3,4,5,-6]
編輯:想想看,以前的解決方案是有缺陷的。 我們只對流式結果感興趣,但在將其發送到下游之前,我們使用S.toList
或L.list
在內存中累積每個單獨的組。 但是如果一組恰好大於機器中的可用內存怎么辦?
這是一個完美的流媒體解決方案,並且與每個組的大小無關:
foo :: Monad m
=> (Int -> Int -> Bool)
-> Stream (Of Int) m ()
-> Stream (Of Int) m ()
foo p =
concats
. S.maps (S.store (\s -> do (total :> r) <- L.purely S.fold L.sum s
S.yield total
return r))
. S.groupBy p
發生了什么變化? 首先,我們使用maps
而不是mapped
,因為現在我們想要轉換子組流,而不是在基礎 monad 中返回結果。
對於每個子組流,我們使用store
在不破壞流的情況下執行求和折疊。 然后我們獲取折疊的結果並將其附加回流,同時還要注意保留maps
要求的原始返回值。
剩下的唯一步驟是使用concats
重新加入子組。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.