Haskell “foldr”和“foldl”能否相互定义?
Haskell “foldr”和“foldl”能否相互定义?,haskell,fold,foldable,Haskell,Fold,Foldable,foldr和foldl可以相互定义吗 Hutton用Haskell编程说 我们需要手动定义什么?的实例的最小完整定义 Foldable类用于定义foldMap或foldr,因为类中的所有其他函数都可以派生 使用默认定义和列表实例从这两个列表中选择一个 那么如何用foldr来定义foldl 是否可以根据foldl定义foldr,以便我们可以通过定义foldl来定义可折叠的类型 为什么在Foldable中,foldMap是根据folddr定义的,而在list Foldable中,foldl的一些专门
foldr
和foldl
可以相互定义吗
Hutton用Haskell编程说
我们需要手动定义什么?的实例的最小完整定义
Foldable
类用于定义foldMap
或foldr
,因为类中的所有其他函数都可以派生
使用默认定义和列表实例从这两个列表中选择一个
那么如何用foldr
来定义foldl
是否可以根据foldl
定义foldr
,以便我们可以通过定义foldl
来定义可折叠的类型
为什么在Foldable
中,foldMap
是根据folddr
定义的,而在list Foldable中,foldl
的一些专门化是根据foldl
定义的:
maximum :: Ord a => [a] -> a
maximum = foldl max
minimum :: Ord a => [a] -> a
minimum = foldl min
sum :: Num a => [a] -> a
sum = foldl (+) 0
product :: Num a => [a] -> a
product = foldl (*) 1
??它们可以改写为
maximum :: Ord a => [a] -> a
maximum = foldr max
minimum :: Ord a => [a] -> a
minimum = foldr min
sum :: Num a => [a] -> a
sum = foldr (+) 0
product :: Num a => [a] -> a
product = foldr (*) 1
谢谢
编辑2:
还有另一种方法可以满足(基于)forfoldl
:
foldl f a list = (foldr construct (\acc -> acc) list) a
where
construct x r = \acc -> r (f acc x)
编辑1
翻转函数的参数不会创建相同的foldr/foldl,这意味着此示例不满足foldr foldl的等式:
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
和foldl
在foldr方面:
foldl' :: Foldable t => (b -> a -> b) -> b -> t a -> b
foldl' f b = foldr (flip f) b
和foldr
:
foldr' :: Foldable t => (a -> b -> b) -> b -> t a -> b
foldr' f b = foldl (flip f) b
反之则不然,因为foldr可能在无限列表上工作,而foldl变体永远无法做到这一点。然而,对于有限列表,虽然在过程中失去了惰性,但foldr也可以用foldl来编写。(欲了解更多信息)
Ando也不满足以下示例:
foldr (-) 2 [8,10] = 8 - (10 - 2) == 0
foldl (flip (-)) 2 [8,10] = (flip (-) (flip (-) 2 8) 10) == 4
一般来说,foldr
和foldl
都不能相互实现。Foldable
的核心操作是foldMap
,所有其他操作都可以从中派生出来。foldr
和foldl
都不够。然而,这种差异只在无限或(部分)未定义结构的情况下才会显现出来,因此有一种倾向是掩盖这一事实
@Damianaltenero已经展示了foldl
和foldr
的“实现”相互之间的关系:
foldl' c = foldr (flip c)
foldr' c = foldl (flip c)
但他们并不总是有正确的行为。考虑列表。然后,foldr(:)[]xs=xs
用于所有xs::[a]
。但是,对于所有的xs
,都是foldr'(:)[]/=xs
,因为foldr'(:)[]xs=foldl(flip(:)nxs
)和foldl
(在列表的情况下)必须遍历列表的整个脊椎才能生成输出。但是,如果xs
是无限的,foldl
无法遍历整个无限列表,因此foldr'(:)[]xs
将永远循环无限xs
,而foldr(:)[]xs
只生成xs
<但是,代码>foldl'=foldl
根据需要。本质上,对于[]
,foldr
是“自然的”,而foldl
是“非自然的”。用foldr
实现foldl
是有效的,因为你正在失去“自然性”,但就foldl
而言,实现foldr
是无效的,因为你无法恢复那种“自然”行为
另一方面,考虑一下
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
没有。用foldl
实现foldr
是可行的(正如我刚才所做的那样),但是你不能用foldr
实现foldl
,因为你无法恢复生产力
foldMap
避免了此问题。对于[]
:
foldMap f [] = mempty
foldMap f (x : xs) = f x <> foldMap f xs
-- foldMap f = foldr (\x r -> f x <> r) mempty
现在,
文档中实际给出了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
用于将ta
中的每个a
转换为b->b
(Endo b
),然后将所有b->b
组合在一起(foldr
以一种方式组合,而foldl
以Dual(Endo b)
向后组合)最后的b->b
应用于初始值z::b
由于性能原因,实例可折叠[]
中的专门化总和
,最小值
等中的折页
替换为折页
。我们的想法是,你无论如何都不能对无限列表求和(这个假设是错误的,但它通常是正确的),所以我们不需要foldr来处理它。在某些情况下,使用foldl
比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
实际上是一个有效、合法的可折叠实例,其中foldr
和foldl
都是不自然的,都不能处理无限结构(foldMap
默认为foldr
,因此也不会处理无限列表)。在这种情况下,foldr
和foldl
可以相互书写(foldl c=foldr(flip c)foldMap (: []) xs = xs -- even for infinite xs
foldMap (Snoc Lin) xs = xs -- even for infinite xs
foldr f z t = appEndo (foldMap (Endo . f) t ) z
foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z
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
instance Foldable [] where
foldr = genFoldr
foldl c = foldr (flip c)
foldl f a l = foldr (\b e c -> e (f c b)) id l a
import Data.Functor.Reverse
import Data.Monoid
data DL a = DL [a] (Reverse [] a)
deriving Foldable
instance Foldable DL where
foldMap f (DL front rear) = foldMap f front <> foldMap f rear
foldMap f (DL front rear) = foldMap f front <> getDual (foldMap (Dual . f) (getReverse rear))
foldr c n (DL xs (Reverse ys)) =
foldr c (foldl (flip c) n ys) xs
foldl f b (DL xs (Reverse ys)) =
foldr (flip f) (foldl f b xs) ys