Haskell迭代器: simple worked example of stripping trailing whitespace

标签 haskell iteration bytestring loops

我试图了解如何将 iteratee 库与 Haskell 一起使用。到目前为止我看到的所有文章似乎都集中在建立如何构建 iteratee 的直觉,这很有帮助,但现在我想真正开始使用它们,我感到有点茫然。查看迭代器的源代码对我来说值(value)有限。

假设我有一个函数可以修剪行中的尾随空格:

import Data.ByteString.Char8

rstrip :: ByteString -> ByteString
rstrip = fst . spanEnd isSpace

我想做的是:将其变成迭代器,读取文件并将其写到其他地方,并从每行中删除尾随空格。我将如何用 iteratee 来构建它?我看到 Data.Iteratee.Char 中有一个 enumLinesBS 函数,我可以深入研究它,但我不知道是否应该使用 mapChunksconvStream 或者如何将上面的函数重新打包到迭代器中。

最佳答案

如果你只想要代码,就是这样:

procFile' iFile oFile = fileDriver (joinI $
   enumLinesBS ><>
   mapChunks (map rstrip) $
   I.mapM_ (B.appendFile oFile))
   iFile

评论:

这是一个三阶段过程:首先将原始流转换为行流,然后应用函数来转换该行流,最后使用该流。自 rstrip处于中间阶段,它将创建一个流转换器(Enumeratee)。

您可以使用 mapChunksconvStream ,但是mapChunks更简单。不同的是mapChunks不允许您跨越 block 边界,而 convStream是比较通用的。我更喜欢convStream因为它没有公开任何底层实现,但是如果 mapChunks就足够了,生成的代码通常更短。

rstripE :: Monad m => Enumeratee [ByteString] [ByteString] m a
rstripE = mapChunks (map rstrip)

注意额外的maprstripE 。外部流(rstrip 的输入)的类型为 [ByteString] ,所以我们需要映射rstrip到它上面。

为了进行比较,如果使用 convStream 实现,则如下所示:

rstripE' :: Enumeratee [ByteString] [ByteString] m a
rstripE' = convStream $ do
  mLine <- I.peek
  maybe (return B.empty) (\line -> I.drop 1 >> return (rstrip line)) mLine

这更长,而且效率较低,因为它一次只会将 rstrip 函数应用于一行,即使可能有更多行可用。可以处理所有当前可用的 block ,这更接近 mapChunks版本:

rstripE'2 :: Enumeratee [ByteString] [ByteString] m a
rstripE'2 = convStream (liftM (map rstrip) getChunk)

无论如何,有了可用的剥离枚举,它就可以很容易地与 enumLinesBS 组合起来。枚举者:

enumStripLines :: Monad m => Enumeratee ByteString [ByteString] m a
enumStripLines = enumLinesBS ><> rstripE

组合运算符 ><>遵循与箭头运算符 >>> 相同的顺序。 enumLinesBS将流分成几行,然后 rstripE剥去它们。现在您只需要添加一个消费者(这是一个普通的迭代器),就完成了:

writer :: FilePath -> Iteratee [ByteString] IO ()
writer fp = I.mapM_ (B.appendFile fp)

processFile iFile oFile =
  enumFile defaultBufSize iFile (joinI $ enumStripLines $ writer oFile) >>= run

fileDriver函数是简单枚举文件并运行生成的 iteratee 的快捷方式(不幸的是参数顺序从 enumFile 切换而来):

procFile2 iFile oFile = fileDriver (joinI $ enumStripLines $ writer oFile) iFile

附录:在这种情况下,您需要 convStream 的额外功能。假设您想将每 2 行连接成一行。您不能使用mapChunks 。考虑当 block 是单例元素时,[bytestring]mapChunks不提供任何访问下一个 block 的方法,因此没有其他东西可以与之连接。与convStream不过,这很简单:

concatPairs = convStream $ do
  line1 <- I.head
  line2 <- I.head
  return $ line1 `B.append` line2

这在应用风格中看起来更好,

convStream $ B.append <$> I.head <*> I.head

你可以想到convStream不断地使用提供的 iteratee 消耗流的一部分,然后将转换后的版本发送给内部消费者。有时甚至这还不够通用,因为每一步都会调用相同的 iteratee。在这种情况下,您可以使用 unfoldConvStream在连续迭代之间传递状态。

convStreamunfoldConvStream还允许单子(monad)操作,因为流处理迭代器是单子(monad)转换器。

关于Haskell迭代器: simple worked example of stripping trailing whitespace,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6643495/

相关文章:

algorithm - 您如何称呼描述列表包含重复项程度的列表的属性?

haskell - 从 Haskell ByteString 中删除 BOM 的最简单方法

haskell - 堆剖面图的可读性

MySQL 与 Haskell

Haskell Recursive HashMap 任意深度的数据结构

python - 将数组列表发送到 For 循环

Python多列表迭代

java - 如何遍历 ArrayList of Objects 的对象 ArrayList?

haskell - ByteString 采用 ISO-8859-1?

haskell - 使用 Haskell 的 Parsec 解析 ByteString