簡體   English   中英

如何編寫Haskell Pipes的“求和”函數?

[英]How to write a Haskell Pipes “sum” function?

我試圖通過編寫自己的sum函數來學習pipes程序包,但我感到很困惑。 我不想使用Pipes.Prelude的實用程序功能(因為它具有sumfold以及其他使它變得微不足道的功能),而僅使用如Pipes.Tutorial所述的信息。 本教程沒有討論Proxy的構造函數,但是如果我查看sumfold的來源,它將使用這些構造函數,並且我想知道是否可以在不了解這些底層細節的情況下編寫sum函數。

只要有可用的值,我就很難確定該函數將如何繼續獲取值,然后以某種方式將總和返回給用戶。 我猜類型是:

sum' :: Monad m => Consumer Int m Int

在我看來這可行,因為此函數可能會消耗值,直到沒有更多值為止,然后返回最終的總和。 我會這樣使用它:

mysum <- runEffect $ inputs >-> sum'

但是, Pipes.Prelude的函數具有以下簽名:

sum :: (Monad m, Num a) => Producer a m () -> m a

所以我想這是我的第一個障礙。 為什么sum函數將Producer作為參數而不是使用>->進行連接?


僅供參考,在達尼迪亞茲回答后,我得出以下結論:

sum' = go 0
  where
    go n p = next p >>= \x -> case x of
      Left _        -> return n
      Right (_, p') -> go (n + 1) p'

Consumers實際上只能做些有限的事情。 他們無法檢測到輸入結束( pipes-parse為此使用了不同的技術),並且當流水線的其他部分停止時(例如上游的Producer,該部分就是必須提供輸入結果的部分。管道。 因此,將總和作為Consumer的返回值通常不會起作用。

一些替代方法是:

  • 實現直接處理Producer內部的功能,或者使用諸如next的輔助功能。 有這種類型的適配器可以將Producer數據提供給“更智能”的消費者,例如foldl包中的Fold

  • 繼續使用Consumer ,但不要將總和作為Consumer的返回值,而應使用WriterT作為基本monad,並將Sum Int monoid作為累加器。 這樣,即使Producer首先停止,您仍然可以運行編寫器以到達累加器。盡管如此,該解決方案的效率可能較低。

WriterT方法的示例代碼:

import Data.Monoid
import Control.Monad
import Control.Monad.Trans.Writer
import Pipes

producer :: Monad m => Producer Int m ()
producer = mapM_ yield [1..10]

summator :: Monad n => Consumer Int (WriterT (Sum Int) n) ()
summator = forever $ await >>= lift . tell .  Sum

main :: IO ()
main = do
   Sum r <- execWriterT . runEffect $ producer >-> summator
   print r

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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