Haskell,Foldr和foldl

时间:2014-11-12 00:02:20

标签: haskell fold

我一直试图将我的头围绕折叠和折叠很长一段时间,我已经决定以下问题应该为我解决。假设您将以下列表[1,2,3]传递给以下四个函数:

a = foldl (\xs y -> 10*xs -y) 0
b = foldl (\xs y -> y - 10 * xs) 0
c = foldr (\y xs -> y - 10 * xs) 0
d = foldr (\y xs -> 10 * xs -y) 0

结果分别为-123,83,281和-321。

为什么会这样?我知道当你将[1,2,3,4]传递给定义为

的函数时
f = foldl (xs x -> xs ++ [f x]) []

它扩展为((([[++] [1])++ [2])++ [3])++ [4]

同样,上述函数a,b,c和d的扩展是什么?

3 个答案:

答案 0 :(得分:1)

我认为Haskell Wiki's fold page上的两张图片很好地解释了它。

由于您的操作不是可交换的,foldrfoldl的结果将不相同,而在交换操作中,它们会:

Prelude> foldl1 (*) [1..3]
6
Prelude> foldr1 (*) [1..3]
6

使用scanlscanr获取包含中间结果的列表是了解发生情况的好方法:

Prelude> scanl1 (*) [1..3]
[1,2,6]
Prelude> scanr1 (*) [1..3]
[6,6,3]

所以在第一种情况下我们有(((1 * 1)* 2)* 3),而在第二种情况下它是(1 *(2 *(1 * 3)))。

答案 1 :(得分:1)

foldr是一个非常简单的函数构思:获取一个结合两个参数的函数,得到一个起点,一个列表,并以这种方式计算在列表上调用函数的结果。

这是关于如何设想foldr电话会议期间发生的事情的一个很好的小提示:

foldr (+) 0 [1,2,3,4,5]
=> 1 + (2 + (3 + (4 + (5 + 0))))

我们都知道[1,2,3,4,5] = 1:2:3:4:5:[]。您需要做的就是用起始点替换[],用我们使用的任何函数替换:。当然,我们也可以用同样的方式重建列表:

foldr (:) [] [1,2,3]
=> 1 : (2 : (3 : []))

如果我们查看签名,我们可以更好地理解函数内发生的事情:

foldr :: (a -> b -> b) -> b -> [a] -> b

我们看到函数首先从列表中获取一个元素,然后是累加器,并返回下一个累加器的内容。有了这个,我们可以编写自己的foldr函数:

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f a []     = a
foldr f a (x:xs) = f x (foldr f a xs)

你在那里;您应该更好地了解foldr的工作原理,以便将其应用于上述问题。

答案 2 :(得分:0)

fold*函数可以被视为循环传递给它的列表,从列表的末尾(foldr)或列表的开头(foldl开始)。对于它找到的每个元素,它将此元素和累加器的当前值传递给您作为lambda函数编写的内容。无论此函数返回什么,都将用作下一次迭代中累加器的值。

略微更改您的符号(acc而不是xs)以显示更清晰的含义,对于第一个左侧折叠

a = foldl (\acc y -> 10*acc - y) 0              [1, 2, 3]
  = foldl (\acc y -> 10*acc - y) (0*1 - 1)      [2, 3]
  = foldl (\acc y -> 10*acc - y) -1             [2, 3]
  = foldl (\acc y -> 10*acc - y) (10*(-1) - 2)  [3]
  = foldl (\acc y -> 10*acc - y) (-12)          [3]
  = foldl (\acc y -> 10*acc - y) (10*(-12) - 3) []
  = foldl (\acc y -> 10*acc - y) (-123)         []
  = (-123)

对于你的第一个右折(注意累加器在lambda函数的参数中采用不同的位置)

c = foldr (\y acc -> y - 10*acc) 0              [1, 2, 3]
  = foldr (\y acc -> y - 10*acc) (3 - 10*0)     [1, 2]
  = foldr (\y acc -> y - 10*acc) 3              [1, 2]
  = foldr (\y acc -> y - 10*acc) (2 - 10*3)     [1]
  = foldr (\y acc -> y - 10*acc) (-28)          [1]
  = foldr (\y acc -> y - 10*acc) (1 - 10*(-28)) []
  = foldr (\y acc -> y - 10*acc) 281            []
  = 281