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)并将其转换为与另一个流一起工作.这两个enumLinesBS和mapStream是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报告错误,或以其他方式处理它们.