我试图了解导管和管道之间的区别。与管道不同,管道有剩菜的概念。剩菜有什么用?我想看看一些剩菜必不可少的例子。
而且由于管道没有剩菜的概念,有没有办法用它们实现类似的行为?
我试图了解导管和管道之间的区别。与管道不同,管道有剩菜的概念。剩菜有什么用?我想看看一些剩菜必不可少的例子。
而且由于管道没有剩菜的概念,有没有办法用它们实现类似的行为?
Gabriella 关于剩余部分总是解析的一部分的观点很有趣。我不确定我是否会同意,但这可能仅取决于解析的定义。
有一大类用例需要剩余部分。解析当然是一个:任何时候解析需要某种前瞻,你就需要剩余部分。一个例子是在 markdown 包的getIndented函数中,它将所有即将出现的行与某个缩进级别隔离开来,剩下的行留待以后处理。
但是,在管道本身中有一组更平凡的例子。每当您处理打包数据(如 ByteString 或 Text)时,您都需要读取一个块,以某种方式对其进行分析,使用剩余的东西来推回额外的内容,然后对原始内容做一些事情。也许最简单的例子是dropWhile。
事实上,我认为 leftover 是流式库的核心、基本功能,以至于管道的新 1.0 接口甚至没有向用户公开禁用 leftovers 的选项。我知道很少有实际用例不需要以某种方式使用它。
我会回答的pipes
。对您的问题的简短回答是,即将推出的pipes-parse
库将支持作为更通用解析框架的一部分的剩余部分。我发现几乎每一种人们想要剩菜的情况,他们实际上都想要一个解析器,这就是为什么我将剩菜问题作为解析的一个子集。您可以在此处找到该库的当前草稿。
但是,如果您想了解pipes-parse
它是如何工作的,实现剩余的最简单方法是仅用于StateP
存储推回缓冲区。这只需要定义以下两个函数:
import Control.Proxy
import Control.Proxy.Trans.State
draw :: (Monad m, Proxy p) => StateP [a] p () a b' b m a
draw = do
s <- get
case s of
[] -> request ()
a:as -> do
put as
return a
unDraw :: (Monad m, Proxy p) => a -> StateP [a] p () a b' b m ()
unDraw a = do
as <- get
put (a:as)
draw
首先查询推回缓冲区以查看是否有任何存储的元素,如果可用则从堆栈中弹出一个元素。如果缓冲区为空,则改为从上游请求一个新元素。当然,如果我们不能推回任何东西,那么拥有缓冲区是没有意义的,所以我们还定义unDraw
了将一个元素推入堆栈以供以后保存。
编辑:哎呀,我忘了包括一个有用的例子,说明剩菜什么时候有用。就像迈克尔说的那样takeWhile
,dropWhile
是剩菜的有用案例。这是drawWhile
函数(类似于迈克尔所说的takeWhile
):
drawWhile :: (Monad m, Proxy p) => (a -> Bool) -> StateP [a] p () a b' b m [a]
drawWhile pred = go
where
go = do
a <- draw
if pred a
then do
as <- go
return (a:as)
else do
unDraw a
return []
现在想象你的制作人是:
producer () = do
respond 1
respond 3
respond 4
respond 6
...并且您将其与使用以下内容的消费者联系起来:
consumer () = do
evens <- drawWhile odd
odds <- drawWhile even
如果第一个drawWhile odd
没有推回它绘制的最终元素,那么您将删除4
,它不会正确传递到第二个drawWhile even
语句`。