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 ,用xreplace[] ,并计算结果。

例如:

 sum [1,2,3] = foldr (+) 0 [1,2,3] [1,2,3] === 1:(2:(3:[])) 

所以

 sum [1,2,3] === 1+(2+(3+0)) = 6 

它有助于理解foldrfoldl之间的区别。 为什么foldr被称为“对折”?

起初我以为是因为它消耗了从右到左的元素。 然而foldrfoldl都从左到右地使用列表。

  • 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的语义是:一个人吃一个已经吃了一条鱼,已经吃了一些藻类的鲨鱼。 食物是蓄电池。

foldlfoldr “剥离”从左到右吃,所以这不是我们把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)

当运营商不交换 foldlfoldr会得到不同的结果。

在你的情况下,第一个例子扩大到(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