首先让我们考虑一下您拥有的函数f。它采用某种累积值,一个普通值,并将它们组合成一个结果。所以,在类型签名中,我们会说a代表累加值的类型,v代表值的类型,r代表结果的类型。
f :: a -> v -> r
现在我们要创建一个使用f 和值列表的折叠函数。
someFold :: (a -> v -> r) -> [v] -> ?
它应该返回什么?它应该产生r 的结果类型,对吧?现在请注意,a 和 r 实际上应该是同一类型,因为我们不断将 f 的结果再次输入到它的第一个参数中。
someFold :: (a -> v -> a) -> [v] -> a
现在缺少一件事。您如何获得第一个a?有两种看待它的方法。要么你只选择第一个值,在这种情况下a 与v 的类型相同,要么你指定一个基值,所以a 实际上可能与v 不同。让我们选择后者,因为这更有趣。让我们也决定在这个列表中从左到右移动。 (这就是你需要的,对吧?)
someFold :: (a -> v -> a) -> a -> [v] -> a
那么...我们如何实现它?这将是递归的,所以让我们从基本情况开始。
someFold f acc [] = acc
如果我们到达列表的末尾,那么我们已经积累了足够多的东西,对吧?那很简单。那么递归情况呢?根据您所说,在每个步骤中,我们应该将f 应用于“迄今为止的累计值”作为第一个参数,并将“列表的第一个值”作为第二个参数。 f acc x。然后我们继续折叠,使用 that 作为我们新的“累积”值。
someFold f acc (x:xs) = someFold f (f acc x) xs
很简单,对吧?但是......如果我们想像你说的那样做并通过获取列表的前两个值来启动函数呢?也很容易。只需取第一个元素,并将其称为原始“基础”累加器!
someFold1 :: (v -> v -> v) -> [v] -> v
someFold1 f (x:xs) = someFold f x xs
请注意,在这种特殊情况下,a 与 v 的类型相同,因此函数 someFold1 有一个非常有趣的类型签名。如果你理解了这个解释,那么恭喜你。我们刚刚实现了foldl and foldl1。
Prelude> foldl1 min "abcde" -- "abcde" is sugar for ['a','b','c','d','e']
'a'
在实际代码中,你应该实际使用foldl' 和朋友。