一般来说,两者都 foldr不能,也foldl不能相互实现。的核心操作Foldableis foldMap,所有其他操作都可以从中派生出来。既不foldr也foldl不够。但是,这种差异仅在无限或(部分)未定义结构的情况下才会显现出来,因此有一种掩盖这一事实的趋势。
@DamianLattenero 展示了彼此的“实现foldl” foldr:
foldl' c = foldr (flip c)
foldr' c = foldl (flip c)
但他们并不总是有正确的行为。考虑列表。然后,foldr (:) [] xs = xs对于所有xs :: [a]. 但是,foldr' (:) [] /= xs对于所有xs, 因为foldr' (:) [] xs = foldl (flip (:)) n xs, and foldl(在列表的情况下)必须遍历列表的整个脊椎才能产生输出。但是,如果xs是无限的,foldl 则无法遍历整个无限列表,因此foldr' (:) [] xs无限循环xs,而foldr (:) [] xs只是产生xs。foldl' = foldl但是,根据需要。本质上,对于[],foldr是“自然的”并且foldl是“不自然的”。实施有效foldl,foldr因为您只是失去了“自然性”,但实施foldr无效foldl,因为您无法恢复“
另一方面,考虑
data Tsil a = Lin | Snoc (Tsil a) a
-- backwards version of data [a] = [] | (:) a [a]
在这种情况下,foldl很自然:
foldl c n Lin = n
foldl c n (Snoc xs x) = c (foldl c n xs) x
而且foldr是不自然的:
foldr c = foldl (flip c)
现在,foldl在无限/部分未定义Tsil的 s 上具有良好的“生产性”行为,而foldr没有。foldr在工作方面实施foldl(就像我在上面所做的那样),但你不能foldl在工作方面实施foldr,因为你无法恢复那种生产力。
foldMap避免了这个问题。对于[]:
foldMap f [] = mempty
foldMap f (x : xs) = f x <> foldMap f xs
-- foldMap f = foldr (\x r -> f x <> r) mempty
对于Tsil:
foldMap f Lin = mempty
foldMap f (Snoc xs x) = foldMap f xs <> f x
-- foldMap f = foldl (\r x -> r <> f x) mempty
现在,
instance Semigroup [a] where
[] <> ys = ys
(x : xs) <> ys = x : (xs <> ys)
-- (<>) = (++)
instance Monoid [a] where mempty = []
instance Semigroup (Tsil a) where
ys <> Lin = ys
ys <> (Snoc xs x) = Snoc (ys <> xs) x
instance Monoid (Tsil a) where mempty = Lin
我们有
foldMap (: []) xs = xs -- even for infinite xs
foldMap (Snoc Lin) xs = xs -- even for infinite xs
实现foldl和foldr实际在文档中给出
foldr f z t = appEndo (foldMap (Endo . f) t ) z
foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z
f用于将ain 中的每个t a转换为b -> b( Endo b),然后将所有b -> bs 组合在一起(foldr以一种方式进行,而foldl用 反向组合它们Dual (Endo b)),然后将 finalb -> b应用于初始值z :: b。
foldr出于性能原因,在 ,中被替换为foldlin specializations sum,minimum等。instance Foldable []这个想法是无论如何你都不能接受sum一个无限列表(这个假设是错误的,但它通常是正确的),所以我们不需要foldr处理它。在某些情况下, usingfoldl比 , 性能更高foldr,因此foldr更改为foldl. 我希望 , 因为Tsil,foldr有时比 , 性能更高foldl, 因此sum,minimum等可以根据foldr, 而不是fold为了获得性能改进而重新实现。请注意,文档说sum,minimum等应该等同于使用foldMap/的形式fold,但可能定义较少,这正是会发生的情况。
有点附录,但我认为值得注意的是:
genFoldr c n [] = n; genFoldr c n (x : xs) = c x (genFoldr c n xs)
instance Foldable [] where
foldl c = genFoldr (flip c)
foldr c = foldl (flip c)
-- similarly for Tsil
实际上是一个有效的合法Foldable实例,其中和都是 不自然的,并且都不能处理无限结构(默认为,因此也不会处理无限列表)。在这种情况下,和可以相互写成(,尽管它是用 实现的)。然而,这个实例是不可取的,因为我们真的想要一个可以处理无限列表的,所以我们改为实现foldrfoldlfoldMapfoldrfoldrfoldlfoldl c = foldr (flip c)genFoldrfoldr
instance Foldable [] where
foldr = genFoldr
foldl c = foldr (flip c)
等式foldr c = foldl (flip c)不再成立的地方。