使用Control.Foldl计算列表中值的频率

时间:2015-08-26 18:34:54

标签: haskell

我正在使用Control.Foldl库来遍历任意长的列表并计算所有出现的任意多个唯一实体。即,列表可以是

形式
[Just "a", Just "b", Just "aab", Nothing, Just "aab"]

我的结果应该是:

[(Just "a",1),(Just "b",1) (Just "aab", 2), (Nothing, 1)]

现在问题是我没有先验地知道这些实体的名称,我想在折叠时动态更新结果。

我的问题是我不知道如何根据Fold中的Control.foldl数据类型来描述此计算。具体来说,在折叠的每个步骤中,我需要遍历结果列表并询问我是否已经看到当前项目,但我认为没有办法使用foldl来描述它。

请注意,为了将来使用,我在这里使用Control.Foldl库非常重要非常,而不是折叠其他可折叠数据类型(如地图)。在某种意义上,我的问题更多的是如何使用Foldl库,因为文档对我来说不太清楚。

编辑:我展示的示例只是一个玩具示例,实际上我需要遍历一个arb大型列表多次计算统计数据,因此我使用foldl库,这允许我使用applicatives组合计算ie toResults <$> stat1 <*> stat2 <*> ... <*> statm $ largeList和foldl只允许我遍历列表一次,计算所有m个统计数据。请使用foldl库找到解决方案。

1 个答案:

答案 0 :(得分:4)

您可以非常简单地将普通foldl'编码为Fold

foldlToFold :: (b -> a -> b) -> b -> Fold a b
foldlToFold f z = Fold f z id

我真的有点疑惑,这个组合器不在图书馆......

无论如何,如果你有

foldl' f z

你可以用

替换它
fold (Fold f z id)

所以在这里,你通常会使用

foldl' (\mp x -> M.insertWith (+) x 1 mp) M.empty

Fold,你正在制作

countingFold :: Ord a => Fold a (Map a Int)
countingFold = Fold (\mp x -> M.insertWith (+) 1 mp) M.empty id

您可以将其用作

countUp :: Ord a => [a] -> Map a Int
countUp = fold countingFold

-- or
countUp = fold (Fold (\mp x -> M.insertWith (+) 1 mp) M.empty id)

如果你想回到最后的列表,你可以

M.toList . countUp

一般情况下,如果您可以将折叠表示为foldl',则可以执行上述转换,以便将其编码为FoldFold更具表现力,因为对于foldl'b类型既是累加器类型又是结果类型;对于Fold,您可以使用单独的累加器和结果类型。

粗略地说,您可以将任何Fold翻译成foldl-and-map:

Fold f z g = map g . foldl' f z

你也可以倒退:

foldlMapToFold :: (b -> a -> b) -> b -> (b -> c) -> Fold a c
foldlMapToFold = Fold

所以,如果你有

map g . foldl' f z

你可以写

fold (Fold f z g)

如果您想使用Fold,请考虑&#34;如何将我的操作描述为foldl'map?&#34;,然后转到从那里。

使用Fold类型优于普通地图和折叠的优势是(除了性能调整)使用他们的Applicative实例组合和操作多个Fold作为对象的能力,以及其他不错的实例也是如Functor,Profunctor,有趣的东西。组合折叠编码为map-and-foldl有点乏味,但Fold包装器允许您使用每个人都知道和喜爱的抽象以更清晰的一流方式完成。

例如,如果我有

fold1 = map g . foldl' f z

fold2 = map g' . foldl' f' z'

我想做

fold3 = map (\(x,y) -> foo (g x) (g' y))
      . foldl' (\(x,x') (y,y) -> (f x y, f' x' y')) (z', z')

(也就是说,在一个pas中对列表进行两次折叠,并在最后用foo重新组合结果。这是一个很大的麻烦,对吧?

但我也可以这样做

fold1 = Fold f z g
fold2 = Fold f' z' g'
fold3 = foo <$> fold1 <*> fold2

(注意,更好的是,使用Fold实际上保持foldl'严格,因为在上面的示例中,延迟元组添加了一个间接层并使得折叠再次偶然)