我希望将相同的数据拆分为两个“分支”,以便单独处理,然后“加入”......
+----------+
+---------+ -->| doublber |--- +--------+
+--------+ | |-- +----------+ -->| | +------+
| source |-->| splitter| | summer |-->| sink |
+--------+ | |-- +----------+ -->| | +------+
+---------+ -->| delayer |--- +--------+
+----------+
我该怎么做?
我的尝试:
import Data.Conduit
import Control.Monad.IO.Class
import qualified Data.Conduit.List as CL
-- import Data.Conduit.Internal (zipSources)
import Control.Arrow ((>>>))
source :: Source IO Int
source = do
x <- liftIO $ getLine
yield (read x)
source
splitter :: Conduit Int IO (Int, Int)
splitter = CL.map $ \x -> (x,x)
doubler = CL.map (* 2)
delayer :: Conduit Int IO Int
delayer = do
yield 0
CL.map id
twoConduitBranches :: Monad m => Conduit a m b -> Conduit c m d -> Conduit (a,b) m (c,d)
twoConduitBranches q w = awaitForever $ \(x, y) -> do
out1 <- undefined q x
out2 <- undefined w y
yield (out1, out2)
summer :: Conduit (Int,Int) IO Int
summer = CL.map $ \(x,y) -> x + y
sink :: Sink Int IO ()
sink = CL.mapM_ (show >>> putStrLn)
-- combosrc = zipSources (source $= delayer) (source $= doubler)
main = source $= splitter $= twoConduitBranches doubler delayer $= summer $$ sink
我应该写什么代替undefined
s?
答案 0 :(得分:3)
你可以做到这一点,但它很丑陋,希望这个实现能够清楚地表明它为什么丑陋而不是管道的内置功能:
twoConduitBranches :: Monad m => Conduit a m c -> Conduit b m d -> Conduit (a,b) m (c,d)
twoConduitBranches q w = getZipConduit
(ZipConduit (CL.map fst =$= q =$= CL.map Left)
<* ZipConduit (CL.map snd =$= w =$= CL.map Right)) =$= collapse
where
collapse = do
v1 <- await
case v1 of
Nothing -> return ()
Just (Left _) -> error "out of sequence 1"
Just (Right d) -> do
v2 <- await
case v2 of
Nothing -> error "mismatched count"
Just (Right _) -> error "out of sequence 2"
Just (Left c) -> do
yield (c, d)
collapse
(注意:我稍微调整了你的类型签名,我认为这是你真正想要的类型签名。)
以下是方法:将q
转换为Conduit
,从每个传入的元组获取第一个值,然后用Left
包装其输出。类似地,我们从每个传入的元组中取第二个值并将其传递给w
,然后用Right
包装输出。
现在这些Conduit
具有相同的类型(它们采用相同的输入元组,并生成相同的Either值),我们使用ZipConduit
组合它们,它们在所有组件之间共享输入将输出合并为一个流。
此流是Either c d
的流,而不是所需的(c, d)
。要进行最终转换,我们使用collapse
。它弹出一个Right
和Left
值,然后将它们组合成一个单元组,并将其生成。
此函数假定输出值序列始终是来自w
的一个值,然后是q
中的一个值。如果发生任何其他事情,它将抛出异常。问题是:管道中没有任何东西暗示它们实际上会以相同的速率产生输出。实际上,管道专门设计用于避免这个假设!
因此,如果您知道您的两个组件管道将始终以相同的速率生成输出,则此功能将起作用。但总的来说,这不是真的。