簡體   English   中英

Haskell 流 - 如何將原始流與結果流合並

[英]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.purelyL.listL.sum來自“foldl”。

畫龍點睛的是從mapped取出每一對([Int],Int)並將其替換為使用for的子流。

讓它工作:

*Main> S.print $ foo (\x y -> x*y>0) $ S.each [-1,-2,3,4,5,-6]

編輯:想想看,以前的解決方案是有缺陷的。 我們只對流式結果感興趣,但在將其發送到下游之前,我們使用S.toListL.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM