如何找到管道的末端?

4
在下面的代码中,我该如何:
  • 更改stdoutCharConsumer,以便在打印完输入流中的所有数据后打印换行符

  • 实现mixinmixin'

而不需要进入Pipes.Internal?这是可能的吗?我需要类似于生产者的next函数。

我使用的是Pipes 4.1.0版本。

#!/usr/bin/env runhaskell
{-# OPTIONS_GHC -Wall #-}

import Pipes

digits, characters :: Monad m => Producer Char m ()
digits = each "0123456789"
characters = each "abcdefghijklmnopqrstuvwxyz"

interleave :: Monad m => Producer a m () -> Producer a m () -> Producer a m ()
interleave a b = do
  n <- lift $ next a
  case n of
    Left () -> b
    Right (x, a') -> do
      yield x
      interleave b a'

stdoutCharConsumer :: Consumer Char IO ()
stdoutCharConsumer = await >>= liftIO . putChar >> stdoutCharConsumer

-- first element of the mixin should go first
mixin :: Monad m => Producer b m () -> Pipe a b m ()
mixin = undefined

-- first element of the pipe should go first
mixin' :: Monad m => Producer b m () -> Pipe a b m ()
mixin' = undefined

main :: IO ()
main = do

    -- this prints "a0b1c2d3e4f5g6h7i8j9klmnopqrstuvwxyz"
    runEffect $ interleave characters digits >-> stdoutCharConsumer
    putStrLn ""

    -- this prints "0a1b2c3d4e5f6g7h8i9jklmnopqrstuvwxyz"
    runEffect $ interleave digits characters >-> stdoutCharConsumer
    putStrLn ""

    -- should print "0a1b2c3d4e5f6g7h8i9jklmnopqrstuvwxyz"
    runEffect $ characters >-> mixin digits >-> stdoutCharConsumer
    putStrLn ""

    -- should print "a1b2c3d4e5f6g7h8i9jklmnopqrstuvwxyz"
    runEffect $ digits >-> mixin characters >-> stdoutCharConsumer
    putStrLn ""

    -- should print "a1b2c3d4e5f6g7h8i9jklmnopqrstuvwxyz"
    runEffect $ characters >-> mixin' digits >-> stdoutCharConsumer
    putStrLn ""

    -- should print "0a1b2c3d4e5f6g7h8i9jklmnopqrstuvwxyz"
    runEffect $ digits >-> mixin' characters >-> stdoutCharConsumer
    putStrLn ""

更新:现在我已经了解了基于推/拉流的知识,我认为即使使用 Pipes.Internal 也不可能实现。这是真的吗?


你理解interleave的工作原理吗?请注意,当管道p完成时,next p会返回Left result - pash
我认为我理解了,但它适用于生产者而不是管道。这就是为什么我将“interleave”留在这里,作为我对生产者拥有的内容以及我希望对管道拥有的内容的示例。 - amakarov
抱歉,我以为next有更一般的类型。如果你深入研究Pipes.Internal,可以查看MonadPlus实例,寻找灵感。也许您可以调整mplus实现背后的思路,以获得更通用的Proxy值类似于interleave的功能。 - pash
1个回答

3

ConsumersPipes 都不知道上游输入结束的情况,你需要使用来自 pipes-parseParser 来解决。

Consumer 相比,Parser 更直接地了解 Producer;它们的 draw 函数(类似于 await)在发现输入结束时返回 Nothing

import qualified Pipes.Parse as P

stdoutCharParser :: P.Parser Char IO ()
stdoutCharParser = P.draw >>= \ma ->
    case ma of 
        Nothing -> liftIO (putStrLn "\n")
        Just c -> liftIO (putChar c) >> stdoutCharParser

为了运行解析器,我们调用evalStateT而不是runEffect

P.evalStateT stdoutCharParser (interleave characters digits) 

关于mixinmixin',我怀疑它们将无法按预期工作。原因是结果Pipe必须知道上游终止才能知道何时产生传递的Producer的剩余值。


可能你是对的。我这样解释。管道不会决定流是否关闭,而是将控制权返回到上游。当上游生产者决定耗尽时,它会直接终止,而不让下面的管道/消费者知道。 - amakarov
@amakarov 正确。pipes-parse 中的 Parsers 将更少的控制权让给了 Producer。它们可以检测到输入结束,并将值推回到 Producer - danidiaz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接