Attoparsec Iteratee

Joh*_*son 10 haskell attoparsec iterate

我想,只是想了解一下Iteratees,重新实现我制作的一个简单的解析器,使用Data.Iteratee和Data.Attoparsec.Iteratee.我很难过.下面我有一个简单的例子,能够解析一个从文件中一行.我的解析器一次读取一行,所以我需要一种向迭代器提供行的方法,直到它完成为止.我已经阅读了所有我发现的谷歌搜索,但是iteratee /枚举器上的很多材料都非常先进.这是重要的代码的一部分:

-- There are more imports above.
import Data.Attoparsec.Iteratee
import Data.Iteratee (joinI, run)
import Data.Iteratee.IO (defaultBufSize, enumFile)

line :: Parser ByteString -- left the implementation out (it doesn't check for 
                             new line)

iter = parserToIteratee line

main = do
    p <- liftM head getArgs
    i <- enumFile defaultBufSize p $ iter
    i' <- run i
    print i'
Run Code Online (Sandbox Code Playgroud)

此示例将从具有多行的文件中解析并打印一行.原始脚本将解析器映射到ByteStrings列表上.所以我想在这里做同样的事情.我enumLines在Iteratee 找到了,但我不能为我的生活弄清楚如何使用它.也许我误解了它的目的?

Joh*_*n L 15

由于您的解析器一次在一行上工作,您甚至不需要使用attoparsec-iteratee.我会这样写:

import Data.Iteratee as I
import Data.Iteratee.Char
import Data.Attoparsec as A

parser :: Parser ParseOutput
type POut = Either String ParseOutput

processLines :: Iteratee ByteString IO [POut]
processLines = joinI $ (enumLinesBS ><> I.mapStream (A.parseOnly parser)) stream2list
Run Code Online (Sandbox Code Playgroud)

理解这一点的关键是"枚举",它只是流转换器的迭代术语.它需要一个流类型的流处理器(iteratee)并将其转换为与另一个流一起工作.这两个enumLinesBSmapStream是enumeratees.

要将解析器映射到多行,mapStream就足够了:

i1 :: Iteratee [ByteString] IO (Iteratee [POut] IO [POut]
i1 = mapStream (A.parseOnly parser) stream2list
Run Code Online (Sandbox Code Playgroud)

嵌套迭代只是意味着它将流转换为流的[ByteString][POut],并且当最终的iteratee(stream2list)运行时,它将该流返回为[POut].所以现在你只需要iteratee等效lines于创建该流[ByteString],这是做什么的enumLinesBS:

i2 :: Iteratee ByteString IO (Iteratee [ByteString] IO (Iteratee [POut] m [POut])))
i2 = enumLinesBS $ mapStream f stream2list
Run Code Online (Sandbox Code Playgroud)

但是由于所有的嵌套,这个功能使用起来非常笨拙.我们真正想要的是一种在流转换器之间直接管道输出的方法,最后将所有内容简化为单个迭代.要做到这一点,我们使用joinI,(><>)以及(><>):

e1 :: Iteratee [POut] IO a -> Iteratee ByteString IO (Iteratee [POut] IO a)
e1 = enumLinesBS ><> mapStream (A.parseOnly parser)

i' :: Iteratee ByteString IO [POut]
i' = joinI $ e1 stream2list
Run Code Online (Sandbox Code Playgroud)

这与我上面编写的内容相同,e1内联.

尽管如此,仍然有重要的因素.此函数只是将解析结果返回到列表中.通常,您会想要做其他事情,例如将结果与折叠结合起来.

编辑:Data.Iteratee.ListLike.mapM_通常对创建消费者很有用.此时,流的每个元素都是一个解析结果,因此如果要打印它们,您可以使用

consumeParse :: Iteratee [POut] IO ()
consumeParse = I.mapM_ (either (\e -> return ()) print)

processLines2 :: Iteratee ByteString IO ()
processLines2 = joinI $ (enumLinesBS ><> I.mapStream (A.parseOnly parser)) consumeParse
Run Code Online (Sandbox Code Playgroud)

这将只打印成功的解析.您可以轻松地向STDERR报告错误,或以其他方式处理它们.