parsec耗尽内存

时间:2015-06-14 03:25:59

标签: haskell out-of-memory parsec

我为一个大型csv文件编写了一个解析器,该文件适用于较小的子集,但内存耗尽约1.5米(实际文件)。 在最初将所有元素解析为列表(使用manyTill)之后,我使用解析器状态将它们存储在单个二叉搜索树中 - 这适用于大文件。

我已经将“元素类型”拆分为三种不同的类型,并希望将它们存储在自己的树中,从而产生三种不同类型的树。 但是,这个版本仅适用于小型测试文件,而较大的测试文件耗尽内存。

import qualified Data.Tree.AVL as AVL
import qualified Text.ParserCombinators.Parsec as Parsec
----
data ENW = ENW (AVL.AVL Extent) (AVL.AVL Node) (AVL.AVL Way)
---- used to be Element = Extent | Node | Way  in a (Tree Element) - this worked
csvParser :: Parsec String ENW ENW
csvParser = do (Parsec.manyTill (parseL) Parsec.eof) >> Parsec.getState
    where parseL = parseLine >> ((Parsec.newline >> return ()) <|> Parsec.eof)

parseLine :: Parsec String ENW ()
parseLine = parseNode <|> parseWay <|> parseExtents

parseNode :: Parsec String ENW ()
parseNode = Parsec.string "node" *> (flip addNode <$> (Node <$> identifier <*> float <*> float)) >>= Parsec.updateState
    where identifier = Parsec.tab *> (read <$> Parsec.many1 Parsec.digit)
          float      = Parsec.tab *> (read <$> parseFloat)

addNode :: ENW -> Node -> ENW
addNode (ENW e n w) node  = (ENW e (AVL.push (sndCC node) node n) w)

parseWay和parseExtent遵循相同的模式,整个事情以

开头
Parsec.runParser csvParser (ENW AVL.empty AVL.empty AVL.empty) "" input

我不明白如何使用三个较小的树而不是一个较大的树可能会导致内存问题。

1 个答案:

答案 0 :(得分:1)

你有充分理由不使用Cassava吗?它可用于传输CSV数据,并且可能比ad hoc CSV解析器更强大。我自己的经验表明它具有出色的性能,可以很容易地扩展到解析你自己的类型。

编辑:看起来你正在使用制表符分隔的值数据,而不是逗号分隔的数据,但是Cassava允许你指定用于拆分列的分隔符。看来你的数据在每行上可能有所不同所以你可能需要使用Cassava的'raw'格式,它为每一行返回一个Vector ByteString,然后你可以根据第一个元素进行解析。

我以前从未见过有人使用过AVL树包,你有没有理由不使用更多的标准结构?该软件包已经过时了(2008年上次更新),更新的软件包可能会表现得更好。