【问题标题】:How does foldl work?折叠是如何工作的?
【发布时间】:2016-06-06 13:48:23
【问题描述】:

谁能解释foldl 的工作原理?
我知道,例如,foldr (-) 0 [1,2,3] 产生 (1 - (2 - (3 - 0))),而 foldl (-) 0 [1,2,3] 产生 (((0 - 1) - 2) - 3),但我还有一些问题:

  • 第一个示例(带有 foldr/foldl 的列表的长度):
    foldr (\_ acc -> acc + 1) 0 [1,2,3,4,5] 按预期生成 5。
    foldl (\_ acc -> acc + 1) 0 [1,2,3,4,5] 生成 6。:|
    foldl (\_ acc -> acc + 1) 0 [2] 生成 3 . :|
    foldl 对这些给定的例子有何反应?

  • 第二个例子:
    foldr (:) [] [1,2,3,4] 产生 [1,2,3,4] - 不用担心,但 foldl (:) [] [1,2,3,4] 给我一个错误:Occurs check: cannot construct the infinite type: a ~ [a]
    foldl 有什么问题?

【问题讨论】:

  • 对于foldl,您需要在acc_ 之间切换参数顺序,因为foldr 在逻辑上将初始参数放在序列的末尾,而foldl 将它放在逻辑上在一开始。这意味着累加器是 foldl 的折叠函数的第一个 (!) 参数(以及 foldr 的第二个参数)。由于这个错误,您的累加器“开始”在1foldl 而不是0。例如,如果您将列表更改为 ["a", "b", "c", "d", "e"],那么您将在 foldl 示例中得到一个很好的类型检查错误。
  • 如果您尝试foldl (\_ acc -> acc + 1) 0 [0,0,0,0],那么您可能会看到您的错误。
  • 谢谢@MicroVirus!
  • @MicroVirus 您应该考虑将该评论作为答案!
  • @pdexter 是的,我的措辞可能有点令人困惑,但我的意思是“元素”1。我在一条评论中将所有内容都扼杀了,所以这不是一个完整的答案。随意写一个完整的答案;适合所有人。

标签: haskell functional-programming fold foldleft


【解决方案1】:

foldr 中,累加器是您要折叠的函数的第二个参数,但在foldl 中,累加器是第一个参数。如果您仔细查看问题的介绍性段落,您可以自己解决这个问题...

“第一个示例”代码具有误导性,因为 acc 参数(其名称暗示它应该是一个累加器)始终是 lambda 的第二个参数,而 foldl 应该是第一个参数。这也令人困惑,因为示例列表元素的类型和值反映了累加器值的类型和值......正如 cmets 所提到的,使用其他值会更好,使用其他类型会更好!

对于“第二个示例”,您会收到类型错误,因为您的参数被交换(并且您不能拥有一个其元素是其自身列表的列表)。手动交换参数顺序:

foldl (\xs x -> x:xs)

或者使用flip,为此设计的库函数:

foldl (flip (:))

注意foldl 的结果应该是一个反向列表(不是复制的),因为foldl 的迭代方向与foldr 的方向相反。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多