融合Haskell中的多个foldl'

时间:2018-06-08 12:05:09

标签: loops haskell lazy-evaluation fold

我正在尝试阅读和分析一个巨大的CSV文件。我使用了来自cassava的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数据并且不会消耗太多内存。有没有办法将foldl融合在一起?那就是

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

take

并且foldl以空地图开头。

内存使用量随着元素数量{{1}} d的增加而增加,无论是否有优化。

1 个答案:

答案 0 :(得分:2)

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