福尔德。foldr函数组合-Haskell
所以,我真的在绞尽脑汁试图理解foldl.foldr的组成。 以下是一个例子:福尔德。foldr函数组合-Haskell,haskell,fold,function-composition,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只加了一次,对吗?(可能不会,但我想知道怎么做/为什么!) 我很困惑(也许是我制造了所有的困惑,哈哈)。 谢谢大家! (
(foldl.foldr) (+) 1 [[1,2,3],[4,5,6]]
结果是22,但这里到底发生了什么
在我看来,这就是正在发生的事情:foldl(+)1[6,15]
。
我的疑问与foldr
部分有关。它不应该将1添加到所有子列表中吗?像这样:foldr(+)1[1,2,3]
。
在我脑子里,1只加了一次,对吗?(可能不会,但我想知道怎么做/为什么!)
我很困惑(也许是我制造了所有的困惑,哈哈)。
谢谢大家!
(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
让我们检查一下
foldl。foldr
。他们的类型是
foldl :: (a -> b -> a) -> (a -> [b] -> a)
foldr :: (c -> d -> d) -> (d -> [c] -> d)
我特意使用了不同类型的变量,并添加了括号,这样我们就可以更清楚地看到,现在我们将它们视为一个参数的函数(它们的结果是函数)。查看foldl
我们看到它是一种提升函数:给定一个函数,使用b
从a
生成a
,我们提升它,使其在[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,效果会更好:我们可以举起多次,就像在
foldl . foldl . foldl . foldl
:: (a -> b -> a) -> (a -> [[[[b]]]] -> a)
实际上,(foldl.foldr)fzxs===foldr fz(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]
我认为这不是正确的申请顺序,丹尼尔<代码>7
不会在您显示的时候就被强制执行,IMO。是的,除非进行优化,否则它将保持一个thunk,直到评估foldl
产生的最终thunk为止。但过早地对其进行评估会减少键入的难度,使其更具可读性。即使对于关联操作,正确的应用程序顺序也很重要,因为它可能会对性能产生(潜在的巨大)影响。