一般来说,两者都 foldr
不能,也foldl
不能相互实现。的核心操作Foldable
is 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
用于将a
in 中的每个t a
转换为b -> b
( Endo b
),然后将所有b -> b
s 组合在一起(foldr
以一种方式进行,而foldl
用 反向组合它们Dual (Endo b)
),然后将 finalb -> b
应用于初始值z :: b
。
foldr
出于性能原因,在 ,中被替换为foldl
in 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
实例,其中和都是 不自然的,并且都不能处理无限结构(默认为,因此也不会处理无限列表)。在这种情况下,和可以相互写成(,尽管它是用 实现的)。然而,这个实例是不可取的,因为我们真的想要一个可以处理无限列表的,所以我们改为实现foldr
foldl
foldMap
foldr
foldr
foldl
foldl c = foldr (flip c)
genFoldr
foldr
instance Foldable [] where
foldr = genFoldr
foldl c = foldr (flip c)
等式foldr c = foldl (flip c)
不再成立的地方。