【问题标题】:Understanding Folds in Haskell了解 Haskell 中的折叠
【发布时间】:2019-02-02 06:30:42
【问题描述】:

根据我对 Haskell 中折叠的了解,foldl (-) 0 [1..5] 通过计算 0-1-2-3-4-5 得到 -15 的结果,foldr (-) 0 [1..5] 通过计算 5-4-3-2-1-0 得到 -5 的结果。那为什么foldl (++) "" ["a", "b", "c"]foldr (++) "" ["a", "b", "c"]都给出"abc"的结果,而foldr的结果却不是"cba"

在理解foldlfoldr 之间的区别时,我是否遗漏了什么?

【问题讨论】:

标签: haskell functional-programming fold binary-operators associativity


【解决方案1】:

我认为this part from the docs 更清楚:


在列表的情况下,foldr 在应用于二元运算符、起始值(通常是运算符的右标识)和列表时,使用二元运算符从右到左减少列表:

foldr f z [x1, x2, ..., xn] == x1 `f` (x2 `f` ... (xn `f` z)...)

。 . .

在列表的情况下,foldl 应用于二元运算符、起始值(通常是运算符的左标识)和列表时,使用二元运算符从左到右减少列表:

foldl f z [x1, x2, ..., xn] == (...((z `f` x1) `f` x2) `f`...) `f` xn

如果您查看示例细分,串联 foldr 相当于:

"a" ++ ("b" ++ ("c" ++ ""))

对于foldl,它相当于:

(("" ++ "a") ++ "b") ++ "c"

对于字符串连接,这些是相同的。


但是,对于减法,

1 - (2 - (3 - 0))

给出不同的结果:

((0 - 1) - 2) - 3

【讨论】:

    【解决方案2】:

    其实foldr (-) 0 [1..5]等于3,因为它是:

    (1 - (2 - (3 - (4 - (5 - 0))))
    

    这个问题的答案在foldr函数的类型中:

    foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
    

    如我们所见,(a -> b -> b) 函数将迭代元素作为第一个参数,将累加器作为第二个参数。这就是为什么使用foldr (++) "" ["a", "b", "c"] 我们有:

    ("a" ++ ("b" ++ ("c" ++ "")))
    

    【讨论】:

    • 关于参数顺序的非常重要的一点。它们的排序使得扩展表达式的参数与列表的顺序相同,并且累加器不必与列表条目的类型相同。例如,any (>5) xs 可以表示为foldr False (\v a -> if v>5 then True else a) xs
    【解决方案3】:

    象征性地被视为折叠的“翻译”,0-1-2-3-4-5 本身是不明确的。 必须指定操作的顺序。

    其实不管是什么操作符,顺序都是

    foldl (-) 0 [1..5] = ((((0 - 1) - 2) - 3) - 4) - 5    -- = -15
    
    foldr (-) 0 [1..5] = 1 - (2 - (3 - (4 - (5 - 0))))    -- = 3
    

    但对于(++),当使用"" 代替0 时,两个排序的结果相同。

    【讨论】:

    • 0-1-2-3-4-5 已定义;它是sum [0, -1, -2, -3, -4, -5],这意味着二进制- 是左关联的(:i (-) 表示infixl 6 -)。因此它与此处的foldl 情况匹配。
    • 当然,这是毫无疑问的。我的意思是,- 象征性地视为任何可能的运算符的替代,如果没有指定关联性(正如 Haskell 中的固定性声明所做的那样),它就没有完全定义。
    【解决方案4】:

    我发现这是获得差异的最有启发性的方法(因为“左联想”的概念对我来说是新的): -- cmets 反映 :doc foldr 和 :doc foldl

    *Main> foldr (-) 2 [1,4,8] -- foldr fz [a,b,c] == a f (b f(c f z))

    3

    *Main> 1 - (4 - ( 8 - 2) ) -- foldr 右关联

    3

    *Main> ((2 - 1) - 4) -8 -- foldl 左结合

    -11

    *Main> foldl (-) 2 [1,4,8] -- foldl fz [a,b,c] == ((z f a) f b) f c

    -11

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多