在 Haskell 中融合多个 foldl'

Fusing multiple foldl' in Haskell

我正在尝试读取和分析一个巨大的 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 有或没有优化。

是的,您确实可以将四个折叠融合在一起,但您必须手动进行。您可以尝试自己写出逻辑,也可以使用库(如 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