通过unsided fold,我的意思是关联运算符的假设原始折叠操作,不保证任何排序。也就是说,(fold + 0 [a b c d])
可以是(+ (+ a b) (+ c d))
或(+ (+ (+ a b) c) d)
。
鉴于此操作是可融合的,具有高度可兼容性和通用性,我认为将它与map
和concat
一起作为我的非递归极简主义语言的唯一列表原语。我已经设法用它来实现大多数列表函数,但不能自己实现双边折叠foldl
/ foldr
。有可能吗?
答案 0 :(得分:7)
如果fold
和map
具有普遍性。这里的标语是foldr is made of monoids实际上,标准的haskell类型类Foldable
实现了foldr
和foldl
in just this way
诀窍在于集合上的一组内同态在函数组合下形成一个monoid,其身份函数为身份。
请注意,foldr
和foldl
本质上是连续的。所以,这个技巧必须放弃你在fold
和map
的实现中的任何并行性。实质上,foldr
到foldMap
的编码是将延迟顺序计算编码为可能无序的计算。这就是为什么我鼓励在可能的情况下使用foldMap
而不是foldr
- 它在可能的情况下支持隐含的并行性,但在表达能力上是等效的。
编辑:将所有内容放在一个地方
我们在a
newtype Endo a = Endo { appEndo :: a -> a }
instance Monoid (Endo a) where
mempty = Endo id
Endo f `mappend` Endo g = Endo (f . g)
然后在折叠中,我们看到foldr
foldr f z t = appEndo (foldMap (Endo . f) t) z
这使用foldMap
类型Monoid m => (a -> m) -> t a -> m
(其中t
是我们正在折叠的集合,我们可以假装它是一个列表,从现在开始提供Monoid m => (a -> m) -> [a] -> m
和相当于
foldMap f ls = fold (map f ls)
其中fold
是幺半群。如果你有一个名为fold' :: (a -> a -> a) -> a -> [a] -> a
的无序折叠,那就是
fold = fold' mappend mempty
所以
foldr f z t = appEndo (foldMap (Endo . f) t) z
= appEndo (fold (map (Endo . f) t)) z
= appEndo (fold' mappend mempty (map (Endo . f) t)) z
= appEndo (fold' (\(Endo f) (Endo g) -> Endo (f . g) (Endo id) (map (Endo . f) t)) z
可以进一步简化为
foldr f z t = (fold' (.) id (map f t)) z
并放弃不必要的parens
foldr f z t = fold' (.) id (map f t) z
这是Daniel Wagner给出的回答。您可以通过类似方式或foldl
实施foldr
。
答案 1 :(得分:3)
foldr f z xs = fold (.) id (map f xs) z
例如,在ghci:
*Dmwit Debug.SimpleReflect> let foldr' f z xs = foldb (.) id (map f xs) z
*Dmwit Debug.SimpleReflect> foldr' f z [w,x,y]
f w (f x (f y z))
*Dmwit Debug.SimpleReflect> foldr f z [w,x,y]
f w (f x (f y z))