【问题标题】:Counting the frequency of values in a list Using Control.Foldl使用 Control.Foldl 计算列表中值的频率
【发布时间】:2015-11-20 22:32:31
【问题描述】:

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

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

我的结果应该是这样的:

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

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

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

请注意,为了将来的使用目的,我在此处使用 Control.Foldl 库真的很重要,而不是折叠其他一些可折叠的数据类型,例如地图。在某种意义上,我的问题更多是关于如何使用 Foldl 库,因为文档对我来说不太清楚。

编辑:我展示的示例只是一个玩具示例,实际上我需要遍历一个任意大列表多次计算统计信息,因此我使用 foldl 库,它允许我使用应用程序组合计算,即 @ 987654327@ 和 foldl 允许我只遍历列表一次,计算所有 m 统计信息。请使用 foldl 库找到解决方案。

【问题讨论】:

  • 我展示的示例只是一个玩具示例,实际上我需要遍历一个任意大列表多次计算统计信息,因此我使用的是 foldl 库,它允许我使用组合计算applicatives ie toResults <$> stat1 <*> stat2 <*> ... <*> statm $ largeList 和 foldl 允许我只遍历列表一次,计算所有 m 统计信息。
  • 一件事不排斥另一件事。您可以将“直方图”设为Map (Maybe String) Int(或任何合适的),但仍使用Fold 来构建它。

标签: haskell


【解决方案1】:

您可以将普通的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,请思考“我如何将我的操作描述为foldl'map?”,然后从那里开始。

使用Fold 类型优于仅使用法线贴图和折叠的优势是(除了性能调整之外)能够使用它们的 Applicative 实例和其他不错的实例将多个 Folds 组合和操作为对象,以及其他不错的实例,例如Functor,Profunctor,诸如此类的有趣的东西。将编码为 maps-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')

(也就是说,一次对列表进行两次折叠,并将最后的结果与foo 重新组合)。这很麻烦,对吧?

但我也可以这样做

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

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

【讨论】:

    猜你喜欢
    • 2020-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多