【发布时间】:2018-06-09 06:15:07
【问题描述】:
我正在尝试读取和分析一个巨大的 CSV 文件。我用的是木薯的Data.Csv.Streaming,函数的应用顺序如下:
Data.ByteString.Lazy.readFile -- Gives lazy stream
Data.Csv.Streaming.decodeByname -- Gives Either String (Header Records t)
\(Right (_, v)) -> v -- Gives right side of either (Records t)
Data.Foldable.toList -- Gives [t]
此后程序进入分析阶段,并执行以下四个(这很重要)不同的实例(即使用不同的过滤器)
filter -- Result of toList is applied through a filter
map
Data.Foldable.foldl' -- Does bin counting using a map. The map has at most 60 keys.
但是,该程序在尝试加载整个 CSV 文件时似乎占用了大量内存。
如果我只执行一个 foldl' 实例,程序会很好地单次遍历 CSV 数据,并且不会消耗太多内存。有没有办法将折叠融合在一起?也就是说,有
x = foldl' f Map.empty $ filter cx li
y = foldl' f Map.empty $ filter cy li
...
并强制它单次执行。
编辑:foldl 中使用了以下函数,Data.Map.Strict 为 Map:
bincollect :: Ord a => Num b => Map.Map a b -> a -> Map.Map a b
bincollect !m !key = Map.insertWith (+) key 1 m
并且 foldl 以空地图开始。
内存使用量随着元素数量的增加而增加 taked 优化或不优化。
【问题讨论】:
-
fold'是一个严格的函数。你可能想看看foldr -
这归功于
foldl'。事实上foldl'是foldl的non-strict 版本。 -
@Welperooni
foldl'不需要将整个列表加载到内存中。用常规的foldl替换它仍然会提供相同的内存使用量。 -
如果你的列表只传给
foldl',应该没问题。foldl'将需要扫描整个列表,但如果没有其他人正在使用该列表,则它可能会被垃圾收集。但是,请确保您传递给foldl'的函数是适当严格的,这样您就不会得到一个巨大的 thunk。例如。您最终的 60 键映射可能会存储惰性 thunk 而不是评估计数。也许您应该发布foldl'代码。 -
只有我一个人认为不应该责备严格吗?对我来说,危险信号是这句话:“在此之后,程序执行以下四个不同的实例......”。除非您在每个实例之后重新读取文件并重新解析它,否则您将无法垃圾收集文件已处理的部分,因此 当然 它会强制整个记忆中的东西。但要确定出了什么问题,我们需要MCVE。
标签: loops haskell lazy-evaluation fold