【发布时间】:2015-06-14 03:25:59
【问题描述】:
我为一个大型 csv 文件编写了一个解析器,该文件适用于较小的子集,但内存不足约 1.5m 行(实际文件)。 在最初将所有元素解析为列表后(使用 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
我不明白使用三棵小树而不是一棵大树会导致内存问题。
【问题讨论】:
-
只是在黑暗中刺伤 - Parsec 数据类型在“用户状态”参数中是严格的,这是您存储数据的位置。当您直接以该用户状态存储树时,在解析时会对其进行严格评估。现在您已经对数据进行了装箱 - 您拥有三棵树是无关紧要的,只是将树放入数据类型中 - 这些树将被延迟评估,这可能会导致各种内存泄漏。尝试使您的数据类型的字段严格。
标签: haskell out-of-memory parsec