我只是想了解一点关于Iteratees的知识,使用Data.Iteratee和Data.Attoparsec.Iteratee重新实现我做的一个简单的解析器。不过我很困惑。下面我有一个简单的示例,它能够解析文件中的一行。我的解析器一次只读取一行,因此我需要一种方法来将行馈送到iteratee,直到完成为止。我已经阅读了所有使用Google搜索的内容,但是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'
本示例将从多行文件中解析并打印一行。原始脚本将解析器映射到ByteString列表上。所以我想在这里做同样的事情。我在Iteratee中找到了
enumLines
,但是我一生都无法弄清楚如何使用它。也许我误会了它的目的? 最佳答案
由于解析器一次只能在一行上工作,因此您甚至不需要使用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
理解这一点的关键是“枚举数”,它只是流转换器的迭代术语。它需要一种流类型的流处理器(迭代器),并将其转换为与另一种流一起使用。
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报告错误,或者也可以通过其他方式处理错误。
关于haskell - Attoparsec Iteratee,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6360963/