我一直试图将我的头围绕折叠和折叠很长一段时间,我已经决定以下问题应该为我解决。假设您将以下列表[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的扩展是什么?
答案 0 :(得分:1)
我认为Haskell Wiki's fold page上的两张图片很好地解释了它。
由于您的操作不是可交换的,foldr
和foldl
的结果将不相同,而在交换操作中,它们会:
Prelude> foldl1 (*) [1..3]
6
Prelude> foldr1 (*) [1..3]
6
使用scanl
和scanr
获取包含中间结果的列表是了解发生情况的好方法:
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