【问题标题】:Fusing multiple foldl' in Haskell在 Haskell 中融合多个 foldl'
【发布时间】: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.StrictMap

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'foldlnon-strict 版本。
  • @Welperooni foldl' 不需要将整个列表加载到内存中。用常规的foldl 替换它仍然会提供相同的内存使用量。
  • 如果你的列表只传给foldl',应该没问题。 foldl' 将需要扫描整个列表,但如果没有其他人正在使用该列表,则它可能会被垃圾收集。但是,请确保您传递给 foldl' 的函数是适当严格的,这样您就不会得到一个巨大的 thunk。例如。您最终的 60 键映射可能会存储惰性 thunk 而不是评估计数。也许您应该发布foldl' 代码。
  • 只有我一个人认为不应该责备严格吗?对我来说,危险信号是这句话:“在此之后,程序执行以下四个不同的实例......”。除非您在每个实例之后重新读取文件并重新解析它,否则您将无法垃圾收集文件已处理的部分,因此 当然 它会强制整个记忆中的东西。但要确定出了什么问题,我们需要MCVE

标签: loops haskell lazy-evaluation fold


【解决方案1】:

是的,您确实可以将四个折叠融合在一起,但您必须手动完成。您可以尝试自己写出逻辑,也可以使用库(如foldl)来提供帮助。例如,您可以将 bincollect 变成折叠:

bincollect :: (Ord a, Num b) => Fold a (Map.Map a b)
bincollect = Fold (\m key -> Map.insertWith (+) key 1 m) Map.empty id

然后,您可以使用prefilter 进行过滤:

x = prefilter cx bincollect

最后,您可以使用Applicative 实例将它们组合在一起:

(w,x,y,z) = fold ((,,,) <$> prefilter cw bincollect
                        <*> prefilter cx bincollect
                        <*> prefilter cy bincollect
                        <*> prefilter cz bincollect)
                 input

【讨论】:

    猜你喜欢
    • 2021-12-20
    • 2019-05-29
    • 2015-01-08
    • 1970-01-01
    • 2016-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-03
    相关资源
    最近更新 更多