使用foldl
了解foldr
函数的实现时遇到了麻烦。我已经阅读了这个问题(Writing foldl using foldr),在下面的例子中我仍然有一些我不理解的东西:
fun :: [Int] -> [Int]
fun l = foldr g (const []) l 1
where g x f lst = if gcd x lst == 1 then x : f x else f lst
该函数将列表作为参数,并返回另一个列表gcd(l[i], l[i + 1] = 1
。
我的问题如下:
1.谁是x
,f
和lst
2.什么是const[]
以及为什么我不能使用id
函数?
答案 0 :(得分:2)
foldr
是像自行车这样奇怪的工具之一,一旦掌握它们就很容易使用,但从一开始就很难学习。经过几年的经验,我已经非常善于发现我可以用foldr
解决的问题,并立即正确地解决它们,但我可能很容易花一些时间来弄清楚究竟是什么我已经做了足够详细的解释!
从实际的角度来看,我通常会用模糊的延续传递语言来考虑foldr
。忽略"简单" foldr
仅应用于三个参数的情况,foldr
的应用程序如下所示:
foldr go finish xs acc1 acc2 ... where
finish acc1 acc2 ... = ?
go x cont acc1 acc2 ... = ?
acc1
等是从左到右传递的累加器""。从概念上讲,结果包括从右到左传递的单个值"。
finish
获取累加器的最终值并生成结果类型的某些内容。它通常是最容易写的部分,因为
foldr go finish [] acc1 acc2 ...
=
finish acc1 acc2 ...
因此,一旦你弄清楚你想要折叠产生什么,写finish
是相当机械的。
go
获取单个容器元素," continuation"和累加器。它传递修改后的值,如果那些累加器"转发"继续获得结果,并使用该结果构建自己的结果。
foldl
是一个特别简单的情况,因为它的go
函数只返回通过使用新的累加器参数折叠容器的其余部分得到的结果。我认为看一个做得更多的例子会更有启发性。这是一个收集数字容器并生成代表运行总和和正在运行的产品的对的列表。
sumsProducts :: (Num n, Foldable f) => f n -> [(n, n)]
sumsProducts xs = foldr go finish xs 0 1
where
finish total prod = [(total, prod)]
go x cont total prod =
(total, prod) : cont (x + total) (x * prod)
答案 1 :(得分:1)
foldr
的类型签名就是这个
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
这意味着应用于其3个参数的foldr
必须返回一个以1
为参数的函数。
因此,您可以将foldr
专门用于此
foldr :: (Int -> (Int -> [Int]) -> (Int -> [Int]))
-> (Int -> [Int])
-> [Int]
-> (Int -> [Int])
这意味着您的g
函数必须具有以下类型
g :: Int -> (Int -> [Int]) -> Int -> [Int]
所以你的参数的类型是
x :: Int
f :: Int -> [Int]
lst :: Int
第二个参数中的foldr
需要Int -> [Int]
而不是Int
,因此您无法将值[]
传递给它。
幸运的是const
返回一个忽略其参数的函数,并且总是返回一个常量表达式
const [] :: a -> [b]
在你的情况下f
确实是某种累加器。但不是减少例如某个数字的值列表,您在这里链接函数。通过最后将1
传递给此函数链,它将被评估,然后构建您在fun
中返回的实际列表。