foldr 是一件容易的事:
foldr :: (a->b->b) -> b -> [a] -> b
它需要一个类似于 (:) 的函数,
(:) :: a -> [a] -> [a]
和一个类似于空列表[]的值,
[] :: [a]
并替换某些列表中的每个 : 和 []。
看起来像这样:
foldr f e (1:2:3:[]) = 1 `f` (2 `f` (3 `f` e))
您也可以将 foldr 想象成某种状态机评估器:
f 是过渡,
f :: input -> state -> state
e 是起始状态。
e :: state
foldr (foldRIGHT) 使用转换 f 和起始状态 e 在输入列表上运行状态机,从右端开始。将中缀符号中的 f 想象为来自-RIGHT 的 pacman。
foldl (foldLEFT) 从左到右执行相同的操作,但转换函数以中缀表示法编写,从右获取其输入参数。所以机器从左端开始消费列表。 Pacman 从左到右使用嘴巴从左到右使用列表,因为嘴巴是 (b->a->b) 而不是 (a->b->b)。
foldl :: (b->a->b) -> b -> [a] -> b
为了清楚起见,将函数 (-) 想象为转换:
foldl (-) 100 [1] = 99 = ((100)-1)
foldl (-) 100 [1,2] = 97 = (( 99)-2) = (((100)-1)-2)
foldl (-) 100 [1,2,3] = 94 = (( 97)-3)
foldl (-) 100 [1,2,3,4] = 90 = (( 94)-4)
foldl (-) 100 [1,2,3,4,5] = 85 = (( 90)-5)
foldr (-) 100 [1] = -99 = (1-(100))
foldr (-) 100 [2,1] = 101 = (2-(-99)) = (2-(1-(100)))
foldr (-) 100 [3,2,1] = -98 = (3-(101))
foldr (-) 100 [4,3,2,1] = 102 = (4-(-98))
foldr (-) 100 [5,4,3,2,1] = -97 = (5-(102))
您可能希望在列表可以无限且评估应该是惰性的情况下使用 foldr:
foldr (either (\l ~(ls,rs)->(l:ls,rs))
(\r ~(ls,rs)->(ls,r:rs))
) ([],[]) :: [Either l r]->([l],[r])
当您使用整个列表来生成其输出时,您可能希望使用 foldl 的严格版本,即 foldl'。由于极长的列表与惰性求值相结合,它可能会表现得更好,并且可能会阻止您出现堆栈溢出或内存不足异常(取决于编译器):
foldl' (+) 0 [1..100000000] = 5000000050000000
foldl (+) 0 [1..100000000] = error "stack overflow or out of memory" -- dont try in ghci
foldr (+) 0 [1..100000000] = error "stack overflow or out of memory" -- dont try in ghci
第一个 - 逐步 - 创建列表的一个条目,对其进行评估并使用它。
第二个先创建一个很长的公式,用 ((...((0+1)+2)+3)+...) 浪费内存,然后计算所有的。
第三个与第二个类似,但使用了另一个公式。