折叠。 foldr函数组合 - Haskell

时间:2013-01-27 20:37:44

标签: haskell fold function-composition

所以,我真的在煎我的大脑,试着理解foldl.foldr的成分。 这是一个例子:

(foldl.foldr) (+) 1 [[1,2,3],[4,5,6]]

结果是22,但这里到底发生了什么?

对我来说,看起来就是这样:foldl (+) 1 [6,15]。 我的疑问与foldr部分有关。它不应该将1添加到所有子列表中吗?像这样:foldr (+) 1 [1,2,3]。 在我脑海中,1只添加了一次,是不是? (可能不是,但我想知道如何/为什么!)。

我很困惑(也许让所有人感到困惑,哈哈)。 谢谢!

3 个答案:

答案 0 :(得分:14)

(foldl.foldr) (+) 1 [[1,2,3],[4,5,6]]

变为

foldl (foldr (+)) 1 [[1,2,3],[4,5,6]]

所以你得到了

foldl (foldr (+)) (foldr (+) 1 [1,2,3]) [[4,5,6]]
foldl

的第一步之后

foldl (foldr (+)) 7 [[4,5,6]]

如果我们评估应用的foldr(除非严格分析器启动,它实际上仍然是未评估的thunk,直到foldl遍历整个列表,但下一个表达式更具可读性它评估),那就变成了

foldl (foldr (+)) (foldr (+) 7 [4,5,6]) []

最后

foldl (foldr (+)) 22 []
~> 22

答案 1 :(得分:13)

让我们来看看foldl . foldr。他们的类型是

foldl :: (a -> b -> a) -> (a -> [b] -> a)
foldr :: (c -> d -> d) -> (d -> [c] -> d)

我故意使用了不同的类型变量,并添加了括号,以便我们现在将它们视为一个参数的函数(它们的结果是函数)变得更加明显。查看foldl我们看到它是一种提升函数:给定一个函数使用aa生成b,我们将其提升以便它可以在{ {1}}(通过重复计算)。函数[b]类似,只是反转了参数。

现在如果我们申请foldr会怎样?首先,让我们派生类型:我们必须统一类型变量,以便foldl . foldr的结果与foldr的参数匹配。所以我们必须替换:foldl

a = d, b = [c]

所以我们得到

foldl :: (d -> [c] -> d) -> (d -> [[c]] -> d)
foldr :: (c -> d   -> d) -> (d -> [c] -> d)

它的含义是什么?首先,foldl . foldr :: (c -> d -> d) -> (d -> [[c]] -> d) 解除类型foldr的参数以处理列表,并反转其参数以便我们得到c -> d -> d。接下来,d -> [c] -> d再次提升此功能以处理foldl - [[c]]列表。

在您的情况下,被提升的操作[c]是关联的,因此我们不关心其应​​用程序的顺序。双提升只是创建一个函数,对所有嵌套元素应用操作。

如果我们只使用(+),效果会更好:我们可以举多次,比如

foldl

答案 2 :(得分:2)

实际上,(foldl.foldr) f z xs === foldr f z (concat $ reverse xs)

即使f是关联操作,正确的应用程序序列也很重要,因为它会对性能产生影响。

我们从

开始
(foldl.foldr) f z xs
foldl (foldr f) z xs

g = foldr f[x1,x2,...,xn_1,xn] = xs写一下,这是

(...((z `g` x1) `g` x2) ... `g` xn)
(`g` xn) ((`g` xn_1) ... ((`g` x1) z) ... )
foldr f z $ concat [xn,xn_1, ..., x1]
foldr f z $ concat $ reverse xs

因此,在您的情况下,正确的缩减序列是

(foldl.foldr) 1 [[1,2,3],[4,5,6]]
4+(5+(6+(  1+(2+(3+  1)))))
22

即便,

Prelude> (foldl.foldr) (:) [] [[1..3],[4..6],[7..8]]
[7,8,4,5,6,1,2,3]

同样,(foldl.foldl) f z xs == foldl f z $ concat xs。使用snoc a b = a++[b]

Prelude> (foldl.foldl) snoc [] [[1..3],[4..6],[7..8]]
[1,2,3,4,5,6,7,8]

此外,(foldl.foldl.foldl) f z xs == (foldl.foldl) (foldl f) z xs == foldl (foldl f) z $ concat xs == (foldl.foldl) f z $ concat xs == foldl f z $ concat (concat xs)等:

Prelude> (foldl.foldl.foldl) snoc [] [[[1..3],[4..6]],[[7..8]]]
[1,2,3,4,5,6,7,8]
Prelude> (foldl.foldr.foldl) snoc [] [[[1..3],[4..6]],[[7..8]]]
[7,8,1,2,3,4,5,6]
Prelude> (foldl.foldl.foldr) (:) [] [[[1..3],[4..6]],[[7..8]]]
[7,8,4,5,6,1,2,3]