我正在玩导管库,并编写了一段代码示例,用于在序列中出现时提取两个数字(2& 3)。以下是我的代码:
import Data.Conduit
import qualified Data.Conduit.List as CL
source = CL.sourceList [1,2,3,4,5,2,3] :: Source IO Int
-- Extract the consequent 2 and 3 number
extract23 :: Conduit Int IO Int
extract23 = do
a <- await
b <- await
case (a,b) of
(Just a,Just b) ->
if a == 2 && b == 3
then do yield a
yield b
extract23
else extract23
_ -> return ()
conduit1 :: Conduit Int IO String
conduit1 = CL.map show
sink1 :: Sink String IO ()
sink1 = CL.mapM_ putStrLn
main :: IO ()
main = source $= (extract23 =$= conduit1) $$ sink1
但是当我执行main
函数时,我没有输出。我的期望实际上是这样的:
2
3
2
3
对我做错了什么的想法?
答案 0 :(得分:4)
您的代码连续两次调用await
。这说的是“在流中给我下两个值,不要把它们放回流中。”当您反复执行此操作时,您实际上是将流分解为2值块。使用原始列表,您基本上可以获得如下所示的元组:
[(1,2),(3,4),(5,2)] -- final 3 is lost since it has no pair
问题是2,3个序列总是落在两个这些元组之间。在我看来,你真正想要的算法是:
目前,您正在逐步推进两个元素。
幸运的是,这个问题有一个简单的解决方案:不是使用await
来获取第二个值,而是同时将其从流中删除,请使用peek
,这将查看价值并把它放回去。如果您将b <- await
替换为b <- CL.peek
,则应该获得您正在寻找的行为。
<强>更新强>
只是提供更多信息。在表面下,peek
在导管中的两个基元之上实现:await
和leftover
,如下所示:
peek = do
mx <- await
case mx of
Nothing -> return Nothing
Just x -> do
leftover x
return (Just x)
通过1个元素展望未来并没有什么神奇之处。您可以同样向前看2个元素。唯一的诀窍是确保以正确的顺序做剩菜:
peek2 = do
mx <- await
my <- await
maybe (return ()) leftover my
maybe (return ()) leftover mx
return (mx, my)