当Tree实现可折叠foldMap时,Foldr / Foldl是免费的吗?

时间:2014-04-27 05:16:28

标签: haskell fold foldable

我是Haskell的初学者,并且学习了#34;了解你一个Haskell"。

关于Tree的{​​{1}}实施,我不了解的事情。

Foldable

来自LYAH的引用:&#34;因此,如果我们只针对某种类型实施instance F.Foldable Tree where foldMap f Empty = mempty foldMap f (Node x l r) = F.foldMap f l `mappend` f x `mappend` F.foldMap f r 我们会在该类型上免费获得foldMapfoldr < / b>&#34;!

有人可以解释一下吗?我不明白我现在免费获得foldlfoldr的方式和原因......

2 个答案:

答案 0 :(得分:25)

我们从foldMap的类型开始:

foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m

foldMap的工作原理是将a -> m函数映射到数据结构上,然后运行它将元素粉碎成mappend的单个累计值。

接下来,我们注意到,给定某种类型bb -> b函数形成一个monoid,(.)作为二进制运算(即mappend)和{{ 1}}作为标识元素(即id。如果您尚未遇到它,mempty被定义为id)。如果我们要为该幺半群专门化id x = x,我们会得到以下类型:

foldMap

(我调用了函数foldEndo :: Foldable t => (a -> (b -> b)) -> t a -> (b -> b) ,因为endofunction是从一种类型到相同类型的函数。)

现在,如果我们查看列表foldEndo

的签名
foldr

我们可以看到foldr :: (a -> b -> b) -> b -> [a] -> b 匹配它,除了对任何foldEndo的推广以及对参数的某些重新排序之外。

在我们开始实施之前,存在技术复杂性,因为Foldable无法直接成为b -> b的实例。要解决此问题,我们使用Monoid中的Endo newtype包装器代替:

Data.Monoid

根据newtype Endo a = Endo { appEndo :: a -> a } instance Monoid (Endo a) where mempty = Endo id Endo f `mappend` Endo g = Endo (f . g) 撰写,Endo只是专门的foldEndo

foldMap

因此,我们会直接跳转至foldEndo :: Foldable t => (a -> Endo b) -> t a -> Endo b ,并根据foldr进行定义。

foldMap

哪个是默认定义you can find in Data.Foldable。最棘手的一点可能是foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b foldr f z t = appEndo (foldMap (Endo . f) t) z ;如果您遇到问题,请将Endo . f视为二元运算符,而将其视为类型为f的一个参数的函数;然后,我们用a -> (b -> b)包装生成的内部函数。

对于Endo,推导基本上是相同的,除了我们使用不同的内部函数monoid,foldl作为二元运算(即我们在相反的方向上组合函数)。

答案 1 :(得分:5)

foldr始终可以定义为:

foldr f z t = appEndo (foldMap (Endo . f) t) z

其中appEndo和Endo只是newtype unwrappers / wrappers。事实上,这段代码直接来自可折叠类型类。因此,通过定义foldMap,您将自动获得foldr。