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

Haskell 使用Control.Foldl计算列表中值的频率,haskell,Haskell,我使用Control.Foldl库遍历任意长的列表,并计算任意多个唯一实体的所有出现次数。也就是说,这份清单可能是正式的 [Just "a", Just "b", Just "aab", Nothing, Just "aab"] 我的结果应该是这样的: [(仅“a”,1),(仅“b”,1)(仅“aab”,2),(无,1)] 现在的问题是,我事先没有这些实体的名称,我希望在折叠时动态更新结果 我的问题是,我不知道如何用Control.foldl中的Fold数据类型来描述此计算。具体来说,在折叠的

我使用
Control.Foldl
库遍历任意长的列表,并计算任意多个唯一实体的所有出现次数。也就是说,这份清单可能是正式的

[Just "a", Just "b", Just "aab", Nothing, Just "aab"]
我的结果应该是这样的:

[(仅“a”,1),(仅“b”,1)(仅“aab”,2),(无,1)]

现在的问题是,我事先没有这些实体的名称,我希望在折叠时动态更新结果

我的问题是,我不知道如何用
Control.foldl
中的
Fold
数据类型来描述此计算。具体来说,在折叠的每个步骤中,我都需要遍历结果列表并询问我是否看到了当前项目,但我看不到使用
foldl
来描述这一点的方法

请注意,出于将来的使用目的,我在这里使用控件.Foldl库非常重要,不要折叠其他可折叠数据类型,如地图。从某种意义上说,我的问题更多的是关于如何使用Foldl库,因为文档对我来说不是很清楚


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

您可以将普通的
foldl'
非常直接地编码为
折叠:

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
使用
折叠
,您将

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'
,则可以执行上面的转换,以便能够将其编码为
折叠
Fold
更具表现力,因为对于
foldl'
b
类型既是累加器又是结果类型;对于
折叠
,可以使用单独的累加器和结果类型

粗略地说,您可以将任何
折叠
转换为foldl和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)
如果要使用
折叠
,请思考“我如何将我的操作描述为
foldl'
map
?”,然后从那里开始

与普通贴图和折叠相比,使用
折叠
类型的优势在于(除了性能调整外)能够使用其应用程序实例以及其他不错的实例,将多个
折叠
作为对象进行组合和操作,如Functor、Profunctor等有趣的东西。将编码为地图的折叠和foldl的折叠结合起来有点单调乏味,但是
折叠
包装器可以让您使用每个人都知道和喜欢的抽象以一种更干净的一流方式来完成

例如,如果我有

fold1 = 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
fold1=折叠f z g
fold2=折叠f'z'g'
fold3=foo fold1 fold2

(请注意,更好的是,使用
Fold
实际上保持了
foldl'
的严格性,因为在上面的示例中,惰性元组添加了一层间接寻址,并使折叠“顺便再次变懒”)

您可以将正常的
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
使用
折叠
,您将

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'
,则可以执行上面的转换,以便能够将其编码为
折叠
Fold
更具表现力,因为对于
foldl'
b
类型既是累加器又是结果类型;对于
折叠
,可以使用单独的累加器和结果类型

粗略地说,您可以将任何
折叠
转换为foldl和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)
如果要使用
折叠
,请思考“我如何将我的操作描述为
foldl'
map
?”,然后从那里开始

与普通贴图和折叠相比,使用
折叠
类型的优势在于(除了性能调整外)能够使用其应用程序实例以及其他不错的实例,将多个
折叠
作为对象进行组合和操作,如Functor、Profunctor等有趣的东西。将编码为地图的折叠和foldl的折叠结合起来有点单调乏味,但是
折叠
包装器可以让您使用每个人都知道和喜欢的抽象以一种更干净的一流方式来完成

例如,如果我有

fold1 = 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
fold1=折叠f z g
fold2=折叠f'z'g'
fold3=foo fold1 fold2

(请注意,更好的是,使用
Fold
实际上保持了
foldl'
的严格性,因为在上面的示例中,惰性元组添加了一层间接寻址,并使折叠“偶然地再次变懒”)

我展示的示例只是一个玩具示例,实际上我需要