我想,只是想了解一下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'
此示例将从具有多行的文件中解析并打印一行。原始脚本将解析器映射到ByteStrings列表上。所以我想在这里做同样的事情。我在Iteratee找到enumLines
,但我不能为我的生活弄清楚如何使用它。也许我误解了它的目的?
答案 0 :(得分: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
理解这一点的关键是“枚举”,它只是流转换器的迭代术语。它需要一个流类型的流处理器(iteratee)并将其转换为与另一个流一起工作。 enumLinesBS
和mapStream
都是枚举。
要将解析器映射到多行,mapStream
就足够了:
i1 :: Iteratee [ByteString] IO (Iteratee [POut] IO [POut]
i1 = mapStream (A.parseOnly parser) stream2list
嵌套迭代只是意味着它将[ByteString]
的流转换为[POut]
的流,并且当最后的iteratee(stream2list)运行时,它将该流返回为[POut]
。所以现在你只需要等同于lines
的iteratee来创建[ByteString]
的流,这是enumLinesBS
所做的:
i2 :: Iteratee ByteString IO (Iteratee [ByteString] IO (Iteratee [POut] m [POut])))
i2 = enumLinesBS $ mapStream f stream2list
但是由于所有的嵌套,这个功能使用起来非常笨拙。我们真正想要的是一种在流转换器之间直接管道输出的方法,最后将所有内容简化为单个迭代。为此,我们使用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
这相当于我上面的编写方式,内联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
这将只打印成功的解析。您可以轻松地向STDERR报告错误,或者以其他方式处理错误。