是否可以使用无边折叠实现foldl / foldr?

时间:2014-01-08 03:07:39

标签: haskell functional-programming mapreduce racket lambda-calculus

通过unsided fold,我的意思是关联运算符的假设原始折叠操作,不保证任何排序。也就是说,(fold + 0 [a b c d])可以是(+ (+ a b) (+ c d))(+ (+ (+ a b) c) d)

鉴于此操作是可融合的,具有高度可兼容性和通用性,我认为将它与mapconcat一起作为我的非递归极简主义语言的唯一列表原语。我已经设法用它来实现大多数列表函数,但不能自己实现双边折叠foldl / foldr。有可能吗?

2 个答案:

答案 0 :(得分:7)

如果foldmap具有普遍性。这里的标语是foldr is made of monoids实际上,标准的haskell类型类Foldable实现了foldrfoldl in just this way

诀窍在于集合上的一组内同态在函数组合下形成一个monoid,其身份函数为身份。

请注意,foldrfoldl本质上是连续的。所以,这个技巧必须放弃你在foldmap的实现中的任何并行性。实质上,foldrfoldMap的编码是将延迟顺序计算编码为可能无序的计算。这就是为什么我鼓励在可能的情况下使用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))