2012-05-31 81 views
9

我有一個簡單的任務 - 從文件中讀出一堆行,並對其中的每一行執行一些操作。除了第一個 - 這是一些標題被忽略。如何在管道中使用導管放置功能?

所以我想我會嘗試導管。

printFile src = runResourceT $ CB.sourceFile src =$= 
    CT.decode CT.utf8 =$= CT.lines =$= CL.mapM_ putStrLn 

很酷。

所以現在我只是要刪除的第一線關閉...而且似乎是一個功能 -

printFile src = runResourceT $ CB.sourceFile src =$= 
    CT.decode CT.utf8 =$= CT.lines =$= drop 1 =$= CL.mapM_ putStrLn 

嗯 - 但現在我發現有滴類型簽名Sink a m()。有人建議,我認爲我可以使用管道和使用降單子實例effectfully下降一些元素 - 所以我想這:

drop' :: Int -> Pipe a a m() 
drop' n = do 
    CL.drop n 
    x <- await 
    case x of 
    Just v -> yield v 
    Nothing -> return() 

這並不類型檢查,因爲管道單子實例只適用於管道同一類型的 - 沉沒有Void作爲他們的輸出,所以我不能像這樣使用它。

我快速瀏覽了管道和管道核心,我注意到管道核心具有我期望的功能,管道是一個最小的庫,但文檔顯示了它將如何實現。

所以我很困惑 - 也許有我缺少一個關鍵的概念。我看到了功能

sequence :: Sink input m output -> Conduit input m output 

但是,這似乎並沒有被正確的想法,作爲輸出值( )

CL.sequence (CL.drop 1) :: Conduit a m()  

我可能就回去使用懶惰-io的,因爲我並不需要任何流 - 但我很想看到正確的方式做到這一點。

回答

6

首先,簡單的答案:

... =$= CT.lines =$= (CL.drop 1 >> CL.mapM_ putStrLn) 

的更詳細的解釋:真的有可以實現drop兩種不同的方式。無論哪種方式,它將首先從輸入中刪除n元素。大約有它的作用下兩個選擇:

  • 說,這是做
  • 開始從輸入流輸出所有剩餘項目的

前者的行爲是什麼Sink將執行(和我們drop實際上做了什麼),而後者是Conduit的行爲。您可以在事實上產生來自前者,後者通過一元組成:

dropConduit n = CL.drop n >> CL.map id 

然後你可以使用dropConduit你描述開頭。這是證明單子組成和融合之間區別的一種好方法;前者允許兩個函數在相同的輸入流上操作,而後者則允許一個函數將流傳送給另一個。

我還沒有做基準測試,但我相當肯定monadic構成會更有效率。

+0

嗯 - 簡單的答案效果很好,謝謝。 dropConduit是'Monad m => Int - > Pipe Void Void m()'我覺得這樣做很難用於我認爲的任何事情? – Oliver

+0

對不起,我正在研究不適用的代碼庫的不同版本。在管道0.4中,你需要有'sinkToPipe(CL.drop n)>> CL.map id'。問題是Data.Conduit.List中的類型過於嚴格。導管0.5將放鬆他們。 –

+0

啊 - 歡呼聲。這就說得通了。 – Oliver