2016-02-01 98 views
8

我試圖通過編寫自己的sum函數來學習pipes包,我很難過。我不想使用Pipes.Prelude中的效用函數(因爲它具有sumfold以及其他使它變得微不足道的函數),並且只使用Pipes.Tutorial中描述的信息。本教程不討論Proxy的構造函數,但如果我查看sumfold的源代碼,它將使用這些構造函數,並且我想知道是否可以在不知道這些低級細節的情況下編寫函數sum如何編寫Haskell Pipes「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作爲參數,而不是使用>->進行連接?


FYI我結束了從danidiaz答案後執行以下操作:

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

好了'sum'在管道中需要的東西,*產生*號,並在一元行動總結起來......這個想法頗爲相似你的想法如果你想 - 它也是這樣設計的包,你不應該在意細節,但使用'fold'和其他提供的原語;) – Carsten

回答

5

Consumers實際上是相當的,他們可以做什麼限制。他們無法檢測到輸入結束(pipes-parse使用不同的技術),當其他部分流水線停止時(例如Producer上游)部分是必須提供結果值的部分管道。因此將總和放在Consumer的返回值中通常不起作用。

一些替代方案:

  • 實現直接與Producer內部是交易,或者使用類似next輔助功能的功能。有這種類型的適配器可以將Producer數據提供給「更智能」的用戶,例如來自foldl包的Fold

  • 繼續使用,但一個Consumer而不是把總和在Consumer的返回值,使用WriterT作爲具有Sum Int幺作爲累加器基單子。這樣,即使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 
+0

「無法檢測到輸入結束」是什麼我失蹤了。非常感謝。 – Ana