2012-08-07 53 views
21

我已經使用zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r')這個,但它被認爲不推薦。組合兩個接收器的首選方法是什麼?

+0

什麼樣的行爲,*確切*,你想要「合併」水槽有?我試着查看'zipSinks'的舊文檔和實現,但行爲不容易一眼就看出來。 – 2012-08-07 15:58:29

+0

@DanBurton:'zipSinks'需要兩個接收器並返回一個接收器,該接收器會產生一個具有相應接收器結果的對。例如'sizeCrc32Sink = zipSinks sizeSink crc32Sink'會計算大小和校驗和。我和Oleg描述的行爲一樣[here](http://okmij.org/ftp/Streams.html#1enum2iter)。 – tymmym 2012-08-07 16:46:56

+0

好吧,我明白了;它基本上掛起了等待並同時將上游輸出饋送到兩個接收器,將輸入流分成兩部分。 Data.Conduit.Util的文檔聲明「現在有更簡單的方法來處理它們的用例」,但我沒有看到這種用例的更簡單的方法,因爲它需要深入研究管道內部實現。 – 2012-08-08 03:46:05

回答

6

((包是conduit-0.5.2.3,整個module只是爲了向下兼容。))


[編輯]

所以,我簡單的一元猜想(見下文)似乎是錯誤,即使類型是正確的。 現在,我只能猜測答案是:

替換功能仍在開發中,幾乎和所有Pipe/Conduit以及類似的概念和庫一樣。

我等待下一個API來解決這個問題,直到那時仍然使用zipSink。 (也許這只是放錯了地方。)

[/編輯]

我不是這個包familar,但不會是做一樣的,因爲這?

zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r') 
zipSinks s1 s2 = (,) <$> s1 <*> s2 

畢竟這是一個Monad。 (FunctorApplicative

zipSinks :: Monad sink => sink r -> sink r' -> sink (r, r') 
zipSinks s1 s2 = liftM2 (,) s1 s2 
+2

類型可以,但不是語義。您的zipSinks版本將連續運行接收器,並且第一個接收器將完全消耗源代碼。 – tymmym 2012-08-15 05:56:26

7

編輯

考慮在此之後,我不認爲這是可能的Data.Conduit的當前版本。管道不是類別,所以&&&是不可能的。我不可能想到從上游獲得結果,逐步將它們提供給兩個接收器,並在第一個接收器結束時進行短路。 (雖然我不認爲Data.Conduit.Util.zipSinks這樣短路,但它似乎是非常可取的。)除了當然,模式匹配在兩個水槽(如包中的zipSinks),但這就是我們試圖避免在這裏。

這就是說,我會在這裏被證明是錯誤的。


這不是很漂亮,但你可以用一種明顯的方式做到這一點。

首先進口:

module Main where 

import Control.Monad.Trans 
import Data.Conduit 
import qualified Data.Conduit.Binary as CB 
import qualified Data.Conduit.List as CL 
import qualified Data.Conduit.Text as CT 
import qualified Data.Conduit.Util as CU 
import Data.Maybe 
import Data.Text (unpack) 

現在對於zipSinks。基本上,您要創建一個接收器,從上游獲取輸入,並將其分別發送給每個子接收器。在這種情況下,我用CL.sourceList來做到這一點。如果await返回Nothing,maybeToList返回一個空列表,所以子接收器也在沒有輸入的情況下運行。最後,每個子宿的輸出被輸入到元組中。

zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r') 
zipSinks s1 s2 = do 
    l <- fmap maybeToList await 
    o1 <- lift $ CL.sourceList l $$ s1 
    o2 <- lift $ CL.sourceList l $$ s2 
    return (o1, o2) 

以下是使用zipSinks的一些示例。它似乎在IO內部以及其外部均正常工作,並且在我進行的少數測試中,輸出與使用舊的zipSinks創建的zipped'的輸出相匹配。

doubleHead :: Monad m => Sink Int m (Maybe Int) 
doubleHead = await >>= return . fmap (2*) 

-- old version 
zipped' :: Monad m => Sink Int m (Maybe Int, Maybe Int) 
zipped' = CU.zipSinks CL.head doubleHead 

-- new version 
zipped :: Monad m => Sink Int m (Maybe Int, Maybe Int) 
zipped = zipSinks CL.head doubleHead 

fromList = CL.sourceList [7, 8, 9] $$ zipped 
-- (Just 7, Just 14) 

fromFile :: String -> IO (Maybe Int, Maybe Int) 
fromFile filename = runResourceT $ 
     CB.sourceFile filename 
    $= CB.lines 
    $= CT.decode CT.utf8 
    $= CL.map (read . unpack) 
    $$ zipped 

-- for a file with the lines: 
-- 
-- 1 
-- 2 
-- 3 
-- 
-- returns (Just 1, Just 2) 
+0

不錯! (注意,你可以爲'doubleHead'寫'await >> = return。fmap(2 *)',同樣''l -fmap mayToList await'而不是在'zipSinks'中使用'input', Data.Conduit.Internals'是一個額外的導入?) – huon 2012-08-15 19:31:29

+0

是的,我意識到我可能可以在某些地方使用函子。對於Haskell來說,我仍然足夠了,這對我來說通常是第二遍編輯,不幸的是。 是的,'Data.Conduits.Internals'是無關的。最初,我正在使用'sinkToPipe'。 感謝您指出這些。我會更新答案。 – Eric 2012-08-15 20:14:41

+2

您的zipSinks版本只包含第一個元素。例如'runResourceT $ CL.sourceList [1,2,3] $$ zipSinks(CL.take 2)(CL.take 2)''會返回'([1],[1])',但應該是'([1 ,2],[1,2])'。 – tymmym 2012-08-16 05:49:07

相關問題