我是Haskell的初学者,并且学习了#34;了解你一个Haskell"。
关于Tree
的{{1}}实施,我不了解的事情。
Foldable
来自LYAH的引用:"因此,如果我们只针对某种类型实施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
,我们会在该类型上免费获得foldMap
和foldr
< / b>&#34;!
有人可以解释一下吗?我不明白我现在免费获得foldl
和foldr
的方式和原因......
答案 0 :(得分:25)
我们从foldMap
的类型开始:
foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
foldMap
的工作原理是将a -> m
函数映射到数据结构上,然后运行它将元素粉碎成mappend
的单个累计值。
接下来,我们注意到,给定某种类型b
,b -> 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。