每个foldl 都是foldr。
让我们记住定义。
foldr :: (a -> s -> s) -> s -> [a] -> s
foldr f s [] = s
foldr f s (a : as) = f a (foldr f s as)
这是列表的标准问题一步迭代器。我曾经让我的学生敲打桌子高呼“你怎么处理空列表?你怎么处理a : as”?这就是你如何弄清楚s 和f 分别是什么。
如果您考虑正在发生的事情,您会发现foldr 有效地计算了f a 函数的大量组合,然后将该组合应用于s。
foldr f s [1, 2, 3]
= f 1 . f 2 . f 3 . id $ s
现在,让我们看看foldl
foldl :: (t -> a -> t) -> t -> [a] -> t
foldl g t [] = t
foldl g t (a : as) = foldl g (g t a) as
这也是对列表的一步迭代,但累加器会随着我们的进行而变化。让我们最后移动它,以便列表参数左侧的所有内容保持不变。
flip . foldl :: (t -> a -> t) -> [a] -> t -> t
flip (foldl g) [] t = t
flip (foldl g) (a : as) t = flip (foldl g) as (g t a)
现在如果我们将= 向左移动一位,我们可以看到一步迭代。
flip . foldl :: (t -> a -> t) -> [a] -> t -> t
flip (foldl g) [] = \ t -> t
flip (foldl g) (a : as) = \ t -> flip (foldl g) as (g t a)
在每种情况下,我们计算如果我们知道累加器会做什么,抽象为\ t ->。对于[],我们将返回t。对于a : as,我们将使用g t a 作为累加器处理尾部。
但现在我们可以将flip (foldl g) 转换为foldr。抽象出递归调用。
flip . foldl :: (t -> a -> t) -> [a] -> t -> t
flip (foldl g) [] = \ t -> t
flip (foldl g) (a : as) = \ t -> s (g t a)
where s = flip (foldl g) as
现在我们可以将其转换为 foldr,其中类型 s 用 t -> t 实例化。
flip . foldl :: (t -> a -> t) -> [a] -> t -> t
flip (foldl g) = foldr (\ a s -> \ t -> s (g t a)) (\ t -> t)
所以s 说“as 会对累加器做什么”,我们会返回 \ t -> s (g t a),即“a : as 对累加器做什么”。向后翻转。
foldl :: (t -> a -> t) -> t -> [a] -> t
foldl g = flip (foldr (\ a s -> \ t -> s (g t a)) (\ t -> t))
Eta-展开。
foldl :: (t -> a -> t) -> t -> [a] -> t
foldl g t as = flip (foldr (\ a s -> \ t -> s (g t a)) (\ t -> t)) t as
减少flip。
foldl :: (t -> a -> t) -> t -> [a] -> t
foldl g t as = foldr (\ a s -> \ t -> s (g t a)) (\ t -> t) as t
所以我们计算“如果我们知道累加器我们会做什么”,然后我们将初始累加器提供给它。
将球打低一点是有一定指导意义的。我们可以摆脱\ t ->。
foldl :: (t -> a -> t) -> t -> [a] -> t
foldl g t as = foldr (\ a s -> s . (`g` a)) id as t
现在让我使用 >>> 从 Control.Arrow 反转该组合。
foldl :: (t -> a -> t) -> t -> [a] -> t
foldl g t as = foldr (\ a s -> (`g` a) >>> s) id as t
也就是说,foldl 计算一个大的reverse 组合。因此,例如,给定[1,2,3],我们得到
foldr (\ a s -> (`g` a) >>> s) id [1,2,3] t
= ((`g` 1) >>> (`g` 2) >>> (`g` 3) >>> id) t
“管道”从左侧输入它的参数,所以我们得到
((`g` 1) >>> (`g` 2) >>> (`g` 3) >>> id) t
= ((`g` 2) >>> (`g` 3) >>> id) (g t 1)
= ((`g` 3) >>> id) (g (g t 1) 2)
= id (g (g (g t 1) 2) 3)
= g (g (g t 1) 2) 3
如果你选择g = flip (:) 和t = [],你会得到
flip (:) (flip (:) (flip (:) [] 1) 2) 3
= flip (:) (flip (:) (1 : []) 2) 3
= flip (:) (2 : 1 : []) 3
= 3 : 2 : 1 : []
= [3, 2, 1]
也就是说,
reverse as = foldr (\ a s -> (a :) >>> s) id as []
通过将foldl 到foldr 的一般 转换实例化。
仅适用于数学家。 执行 cabal install newtype 并导入 Data.Monoid、Data.Foldable 和 Control.Newtype。添加悲惨丢失的实例:
instance Newtype (Dual o) o where
pack = Dual
unpack = getDual
观察到,一方面,我们可以通过foldr 实现foldMap
foldMap :: Monoid x => (a -> x) -> [a] -> x
foldMap f = foldr (mappend . f) mempty
反之亦然
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f = flip (ala' Endo foldMap f)
这样foldr 在组成内函数的幺半群中累积,但现在要得到foldl,我们告诉foldMap 在Dual 中工作。
foldl :: (b -> a -> b) -> b -> [a] -> b
foldl g = flip (ala' Endo (ala' Dual foldMap) (flip g))
mappend 对应于 Dual (Endo b) 是什么?模换行,正好是逆向组合,>>>。