foldr如何工作?
任何人都可以解释foldr
如何工作的?
拿这些例子来说:
Prelude> foldr (-) 54 [10, 11] 53 Prelude> foldr (\xy -> (x+y)/2) 54 [12, 4, 10, 6] 12.0
我对这些处决感到困惑。 有什么build议么?
foldr
从列表的右端开始,并使用您提供的函数将每个列表条目与累加器值组合在一起。 结果是在所有列表元素中“折叠”之后累加器的最终值。 它的types是:
foldr :: (a -> b -> b) -> b -> [a] -> b
从这里你可以看到list元素(typesa
)是给定函数的第一个参数,而累加器(typesb
)是第二个。
举个例子:
Starting accumulator = 54 11 - 54 = -43 10 - (-43) = 53 ^ Result from the previous line ^ Next list item
所以你得到的答案是53。
第二个例子:
Starting accumulator = 54 (6 + 54) / 2 = 30 (10 + 30) / 2 = 20 (4 + 20) / 2 = 12 (12 + 12) / 2 = 12
所以结果是12。
编辑:我打算补充,这是有限的名单。 foldr
也可以在无限清单上工作,但是我认为最好先把你的头部放在有限的情况下。
理解foldr最简单的方法是重写没有糖的折叠列表。
[1,2,3,4,5] => 1:(2:(3:(4:(5:[]))))
现在foldr fx
所做的就是用中缀formsreplacef
,用x
replace[]
,并计算结果。
例如:
sum [1,2,3] = foldr (+) 0 [1,2,3] [1,2,3] === 1:(2:(3:[]))
所以
sum [1,2,3] === 1+(2+(3+0)) = 6
它有助于理解foldr
和foldl
之间的区别。 为什么foldr
被称为“对折”?
起初我以为是因为它消耗了从右到左的元素。 然而foldr
和foldl
都从左到右地使用列表。
-
foldl
从左到右评估 (左联合) -
foldr
评估从右到左(右关联)
我们可以用一个使用关联性重要的操作符的例子来明确这个区别。 我们可以使用一个人的例子,比如操作符“吃”:
foodChain = (human : (shark : (fish : (algae : [])))) foldl step [] foodChain where step eater food = eater `eats` food -- note that "eater" is the accumulator and "food" is the element foldl `eats` [] (human : (shark : (fish : (algae : [])))) == foldl eats (human `eats` shark) (fish : (algae : [])) == foldl eats ((human `eats` shark) `eats` fish) (algae : []) == foldl eats (((human `eats` shark) `eats` fish) `eats` algae) [] == (((human `eats` shark) `eats` fish) `eats` algae)
这个foldl
的语义是:一个人吃一些鲨鱼,然后吃同一个鲨鱼的人,然后吃一些鱼,等等。
与此相对照:
foldr step [] foodChain where step food eater = eater `eats` food. -- note that "eater" is the element and "food" is the accumulator foldr `eats` [] (human : (shark : (fish : (algae : [])))) == foldr eats (human `eats` shark) (fish : (algae : [])))) == foldr eats (human `eats` (shark `eats` (fish)) (algae : []) == foldr eats (human `eats` (shark `eats` (fish `eats` algae))) [] == (human `eats` (shark `eats` (fish `eats` algae)
这个foldr
的语义是:一个人吃一个已经吃了一条鱼,已经吃了一些藻类的鲨鱼。 食物是蓄电池。
foldl
和foldr
“剥离”从左到右吃,所以这不是我们把foldl称为“left fold”的原因。 相反,评估顺序很重要。
想想foldr
的定义 :
-- if the list is empty, the result is the initial value z foldr fz [] = z -- if not, apply f to the first element and the result of folding the rest foldr fz (x:xs) = fx (foldr fz xs)
因此,例如foldr (-) 54 [10,11]
必须等于(-) 10 (foldr (-) 54 [11])
,即再扩大,等于(-) 10 ((-) 11 54)
。 所以内部操作是11 - 54
,也就是-43; 而外部操作是10 - (-43)
,即10 + 43
,因此你观察53
。 为你的第二种情况,通过类似的步骤,再次,你会看到结果如何形成!
foldr (-) 0 [1, 2, 3]
产生(1 - (2 - (3 - 0)))
。 比较foldl
产生(((0 - 1) - 2) - 3)
foldl
(((0 - 1) - 2) - 3)
。
当运营商不交换 foldl
和foldr
会得到不同的结果。
在你的情况下,第一个例子扩大到(10 - (11 - 54))
,这给了53。
理解foldr
一个简单方法是这样的:它用提供的函数的应用程序replace每个列表构造函数。 你的第一个例子将转化为:
10 - (11 - 54)
从:
10 : (11 : [])
我从Haskell的Wikibook中得到的一条很好的build议可能在这里有一些用处:
作为一个规则,你应该在可能是无限的列表上使用
foldr
,或者在折叠构build数据结构的地方使用foldr
,如果列表已知是有限的,并且归结为单个值,则折叠。foldl
(没有蜱)应该很less使用。
我一直认为http://foldr.com是一个有趣的插图。; 看到Lambda终极职位。
好的,让我们看看参数:
- 一个函数(它接受一个列表元素和一个与它返回的值相同types的值(可能的部分结果));
- 空列表特例初始结果的说明
- 一个列表;
返回值:
- 一些最终的结果
它首先将该函数应用于列表中的最后一个元素和空列表结果。 然后它重新使用这个结果和前一个元素的函数,等等,直到它需要一些当前的结果,并且列表的第一个元素返回最终的结果。
使用一个带有元素和前面一些折叠结果的函数,折叠“折叠”一个包含初始结果的列表。 它为每个元素重复这一点。 所以,foldr会从列表的末尾开始,或者从右侧开始。
folr f emptyresult [1,2,3,4]
变成f(1, f(2, f(3, f(4, emptyresult) ) ) )
。 现在只需在括号中进行评估就是了。
需要注意的一点是,提供的函数f
必须处理自己的返回值作为第二个参数,这意味着两者必须具有相同的types。
来源: 我的post ,如果你认为它可能有帮助,我从一个命令性的不安全的JavaScriptangular度来看待它。
我认为以简单的方式实现地图,foldl和foldr有助于解释它们是如何工作的。 工作的例子也有助于我们的理解。
myMap f [] = [] myMap f (x:xs) = fx : myMap f xs myFoldL fi [] = i myFoldL fi (x:xs) = myFoldL f (fix) xs > tail [1,2,3,4] ==> [2,3,4] > last [1,2,3,4] ==> 4 > head [1,2,3,4] ==> 1 > init [1,2,3,4] ==> [1,2,3] -- where f is a function, -- acc is an accumulator which is given initially -- l is a list. -- myFoldR' f acc [] = acc myFoldR' f acc l = myFoldR' f (f acc (last l)) (init l) myFoldR fz [] = z myFoldR fz (x:xs) = fx (myFoldR fz xs) > map (\x -> x/2) [12,4,10,6] ==> [6.0,2.0,5.0,3.0] > myMap (\x -> x/2) [12,4,10,6] ==> [6.0,2.0,5.0,3.0] > foldl (\xy -> (x+y)/2) 54 [12, 4, 10, 6] ==> 10.125 > myFoldL (\xy -> (x+y)/2) 54 [12, 4, 10, 6] ==> 10.125 foldl from above: Starting accumulator = 54 (12 + 54) / 2 = 33 (4 + 33) / 2 = 18.5 (10 + 18.5) / 2 = 14.25 (6 + 14.25) / 2 = 10.125` > foldr (++) "5" ["1", "2", "3", "4"] ==> "12345" > foldl (++) "5" ["1", "2", "3", "4"] ==> “51234" > foldr (\xy -> (x+y)/2) 54 [12,4,10,6] ==> 12 > myFoldR' (\xy -> (x+y)/2) 54 [12,4,10,6] ==> 12 > myFoldR (\xy -> (x+y)/2) 54 [12,4,10,6] ==> 12 foldr from above: Starting accumulator = 54 (6 + 54) / 2 = 30 (10 + 30) / 2 = 20 (4 + 20) / 2 = 12 (12 + 12) / 2 = 12