fold f [a,b,c,d] = (f (f a b) (f c d))
即,对二元关联函数进行操作的折叠(因此应用顺序无关紧要)。如果我没记错的话,这在数据库中很常见,因为它可以并行化。所以,关于它,我问:
答案 0 :(得分:5)
这通常被认为是树木减少,并且在并行计算中很重要,因为它体现了分而治之的减少。
首先,如果组合函数是非关联的,那么foldl
,foldr
和“未组合折叠”之间显然存在很大差异,所以让我们假设我们结合了一个关联操作。立即,所有折叠都可以用Monoid
表示。
foldlm :: Monoid m => [m] -> m
foldlm = foldl mappend mempty
foldrm :: Monoid m => [m] -> m
foldrm = foldr mappend mempty
usfoldm :: Monoid m => [m] -> m
usfoldm = foldTree mappend mempty . buildTree
foldMap :: Monoid m => (a -> m) -> [a] -> m
更好地代表了哪个,foldr
默认使用foldMap f = foldr (mappend . f) mempty
定义。
Monoid
如果给出最终提取步骤,只要在树状序列类型上定义Monoid
来控制元素 - data Tree a
singleton :: a -> Tree a
instance Monoid (Tree a) where ...
foldTree :: Monoid a => Tree a -> a
foldTree . foldMap singleton :: Monoid a => [a] -> a
的组合方式,这就足以产生树状无边折叠
foldMap
最后,我们已经看到我们可以从foldr
获得foldr
,但我们也可以从foldMap
获得newtype Endo a = Endo { appEndo :: a -> a }
instance Monoid (Endo a) where
mempty = id
mappend (Endo f) (Endo g) = Endo (f . g)
foldr f z as = appEndo (foldMap (Endo . f) as) z
foldMap
通常,Monoid
被认为更原始,因为它让底层foldMap
选择其首选折叠方法。这意味着我们可以在每个数据类型级别上自由编写更高效或更平行的折叠,但这样做仍然可能具有挑战性。
值得注意的是,Foldable
抽象通常被发现为Foldable
的实例方法,这是一种非常流行但更新的Haskell类型类。尽管它具有实用性,但它也被认为有点愚蠢,因为toList :: Foldable f => f a -> [a]
除了那个之外几乎没有什么有意义的法则
Monoid
存在,这也让我们看到foldMap
的{{1}}性质,因为[a]
是我们可以使用Monoid
恢复的通用foldr
。< / p>
为了进一步研究融合规则,阅读Gershom Bazerman的Building up to a Point via Adjunctions中提出的双类型Buildable
是很有价值的。
最后,至于流行度,我认为这绝对是这些天实例化Foldable
的首选方法,因为它可以在必要时允许更高效的Monoid
折叠,但它肯定比{{1}更新和foldl
这可能会导致其相对模糊。
答案 1 :(得分:3)
您正在寻找的功能是Data.Foldable.foldMap
。
foldMap :: Data.Monoid.Monoid m => (a -> m) -> t a -> m
此函数与foldr
同构。作为证据,请注意foldMap
或foldr
中的一个是Foldable
的最小完整定义,这意味着每个都可以用另一个来编写。这肯定地回答了你的前两个问题。
我没有具体了解foldMap
的融合规则,但我确信可以存在。foldl
。至少,foldr融合规则应该在某种程度上适用。
我不知道为什么勉强提到它。
值得一提的一个考虑因素是,就列表而言,您无法始终充分利用此折叠。由于列表是由cons单元格构成的,所以进行树折叠意味着遍历列表的一半,然后向下递减每一半并再次遍历一半,等等。与foldr
或{{1相比,这是一些额外的遍历}}。对于非列表结构,树折可以更多更高效,甚至对于列表,它可以利用这一点。最近有一个很好的blog关于这样的任务。