9

我有一个简单的任务——从一个文件中读取一堆行并对每一行做一些事情。除了第一个 - 这是一些被忽略的标题。

所以我想我会尝试管道。

printFile src = runResourceT $ CB.sourceFile src =$= 
    CT.decode CT.utf8 =$= CT.lines =$= CL.mapM_ putStrLn

凉爽的。

所以现在我只想删除第一行......而且似乎有一个功能 -

printFile src = runResourceT $ CB.sourceFile src =$= 
    CT.decode CT.utf8 =$= CT.lines =$= drop 1 =$= CL.mapM_ putStrLn

嗯-但现在我注意到 drop 具有类型签名Sink a m ()。有人建议我可以将 Monad 实例用于管道并使用 drop 有效地删除一些元素 - 所以我尝试了这个:

drop' :: Int -> Pipe a a m ()
drop' n = do
  CL.drop n
  x <- await
  case x of 
    Just v -> yield v
    Nothing -> return ()

哪个不进行类型检查,因为管道的 monad 实例仅适用于相同类型的管道 - 接收器的输出为 Void,所以我不能这样使用它。

我快速浏览了管道和管道核心,我注意到管道核心具有我预期的功能,管道是一个最小的库,但文档显示了它是如何实现的。

所以我很困惑-也许我缺少一个关键概念..我看到了这个功能

sequence ::  Sink input m output -> Conduit input m output

但这似乎不是正确的想法,因为输出值为 ()

CL.sequence (CL.drop 1) :: Conduit a m ()    

我可能会回去使用lazy-io,因为我真的不需要任何流媒体——但我很想看看正确的方法。

4

1 回答 1

6

首先,简单的回答:

... =$= CT.lines =$= (CL.drop 1 >> CL.mapM_ putStrLn)

更长的解释:实际上有两种不同的方式可以实现dropn无论哪种方式,它都会首先从输入中删除元素。接下来有两种选择:

  • 说完成了
  • 开始从输入流中输出所有剩余的项目

前一种行为是 aSink将执行的(以及我们drop实际执行的),而后者是 a 的行为Conduit。实际上,您可以通过一元组合从前者生成后者:

dropConduit n = CL.drop n >> CL.map id

然后你可以dropConduit像你在开始时描述的那样使用。这是展示一元组合和融合之间差异的好方法;前者允许两个函数对同一个输入流进行操作,而后者允许一个函数向另一个函数提供一个流。

我没有进行基准测试,但我相当肯定一元组合会更有效率。

于 2012-05-31T15:21:19.143 回答