gpt4 book ai didi

haskell - 具有多个输入的熔断导管

转载 作者:行者123 更新时间:2023-12-04 04:18:00 24 4
gpt4 key购买 nike

我正在尝试创建一个可以使用多个输入流的管道。我需要能够以无特定顺序(例如,不交替)等待一个或另一个输入流,从而使 zip 无用。这里没有任何并行或不确定的事情发生:我在一个流或另一个流上等待。我希望能够编写类似于以下的代码(其中 awaitAawaitB 分别在第一个或第二个输入流上等待):

do
_ <- awaitA
x <- awaitA
y <- awaitB
yield (x,y)
_ <- awaitB
_ <- awaitB
y' <- awaitB
yield (x,y')

我最好的解决方案是让内部单子(monad)成为另一个管道,例如
foo :: Sink i1 (ConduitM i2 o m) ()

然后允许
awaitA = await
awaitB = lift await

这主要是有效的。不幸的是,这似乎使得在外导管完全连接之前很难熔合到内导管。我尝试的第一件事是:
fuseInner :: Monad m =>
Conduit i2' m i2 ->
Sink i1 (ConduitM i2 o m) () ->
Sink i1 (ConduitM i2' o m) ()
fuseInner x = transPipe (x =$=)

但这不起作用,至少在 x 时从 (x =$=) 开始是有状态的多次运行,有效地重新启动 x每一次。

有没有办法写 fuseInner,而不是闯入导管的内部(看起来会很乱)?有没有更好的方法来处理多个输入流?我只是远远超出了管道的设计目的吗?

谢谢!

最佳答案

如果要合并两个 IO -生成的流,然后加布里埃尔的评论就是解决方案。

否则,您不能等待两个流,哪个流首先产生一个值。导管是单线程和确定性的 - 它一次只处理一个管道。但是您可以创建一个函数来交错两个流,让它们决定何时切换:

{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
import Control.Monad (liftM)
import Data.Conduit.Internal (
Pipe (..), Source, Sink,
injectLeftovers, ConduitM (..),
mapOutput, mapOutputMaybe
)

-- | Alternate two given sources, running one until it yields `Nothing`,
-- then switching to the other one.
merge :: Monad m
=> Source m (Maybe a)
-> Source m (Maybe b)
-> Source m (Either a b)
merge (ConduitM l) (ConduitM r) = ConduitM $ goL l r
where
goL :: Monad m => Pipe () () (Maybe a) () m ()
-> Pipe () () (Maybe b) () m ()
-> Pipe () () (Either a b) () m ()
goL (Leftover l ()) r = goL l r
goL (NeedInput _ c) r = goL (c ()) r
goL (PipeM mx) r = PipeM $ liftM (`goL` r) mx
goL (Done _) r = mapOutputMaybe (liftM Right) r
goL (HaveOutput c f (Just o)) r = HaveOutput (goL c r) f (Left o)
goL (HaveOutput c f Nothing) r = goR c r
-- This is just a mirror copy of goL. We should combine them together to
-- avoid code repetition.
goR :: Monad m => Pipe () () (Maybe a) () m ()
-> Pipe () () (Maybe b) () m ()
-> Pipe () () (Either a b) () m ()
goR l (Leftover r ()) = goR l r
goR l (NeedInput _ c) = goR l (c ())
goR l (PipeM mx) = PipeM $ liftM (goR l) mx
goR l (Done _) = mapOutputMaybe (liftM Left) l
goR l (HaveOutput c f (Just o)) = HaveOutput (goR l c) f (Right o)
goR l (HaveOutput c f Nothing) = goL l c

它处理一个源直到它返回 Nothing ,然后切换到另一个,依此类推。如果一个源完成,则另一个源被处理到最后。

例如,我们可以组合和交错两个列表:
import Control.Monad.Trans
import Data.Conduit (($$), awaitForever)
import Data.Conduit.List (sourceList)

main = (merge (sourceList $ concatMap (\x -> [Just x, Just x, Nothing]) [ 1..10])
(sourceList $ concatMap (\x -> [Just x, Nothing]) [101..110]) )
$$ awaitForever (\x -> lift $ print x)

如果您需要多个来源, merge可以适应类似的东西
mergeList :: Monad m => [Source m (Maybe a)] -> Source m a

这将在给定的源列表中循环,直到它们全部完成。

关于haskell - 具有多个输入的熔断导管,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15594556/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com