折叠是一个函数,它获取结构中的一段数据,并将其折叠为另一段数据。通常我们这样做是为了将集合“减少”为单个值。这就是为什么如果您查看其他语言,如 Lisp、Smalltalk、Ruby、JavaScript 等,您会发现这个名为 reduce 的操作,它是 Haskell 中折叠的可怜表亲。
我说这是可怜的表弟,因为您对列表的直觉是正确的,但是在 Haskell 中,我们更加抽象和通用,因此我们的折叠函数可以在任何类型的结构上工作,我们已经告诉 Haskell 的类型是什么折叠意味着为。
因此,我们可以谈论“使用加法和折叠将数字列表转换为和值”,或者我们可以谈论“使用函数获取名称的家谱并将其折叠成列表”,等等等等。每当我们有将某物的结构更改为单个值或可能更改为不同的结构化值集的想法时,这就是折叠。
在 Haskell 中表示这一点的“规范”方式是 foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b,但它更容易,就像您认为在开头使用“a”作为 Foldable f => t a 类型一样,因为这样更容易理解。所以我们有一个专门的类型foldr :: (a -> b -> b) -> b -> [a] -> b。但是 a 和 b 是什么? (a -> b -> b) 是什么,这三个参数有什么作用?
让我们将它专门用于Int 值,用于a 和b:foldr :: (Int -> Int -> Int) -> Int -> [Int] -> Int 哇...这使它...有趣不是吗?所以foldr 采用两个整数的函数,例如(+) 函数,它采用单个Int 值(这是它将用作目标结果的初始值,以及@987654337 的列表@values... 然后它将产生一个 Int... 也就是说,它采用 Int -> Int -> Int 函数并将其应用于单个 Int 和第一个 [Int],然后应用那个函数到那个结果和[Int]的下一个值等等,直到没有更多的[Int]离开......然后这就是它返回的内容。
实际上是在数据结构上折叠函数。
这对列表来说很好,它是一条直线,但你所拥有的是一棵树,而不是列表。那么它是如何在那里工作的呢?好吧,让我们看看我们如何专门化foldr 以从Int 的列表中生成一对最高和最低数字? foldr :: (Int -> (Int, Int) -> (Int, Int)) -> (Int, Int) -> [Int] -> (Int, Int)。所以我们采用一个函数,它接受一个Int 和一对,我们将初始对以及来自[Int] 的第一个Int 放入其中。这会返回给我们一个新的配对,然后我们对[Int] 的 下一个 执行相同的过程,然后我们继续该过程,直到最后只剩下一对。
foldToMinMax = foldr (\newNum (minnum,maxnum) -> (min minnum newNum, max maxnum newNum)) (maxBound :: Int, minBound :: Int)
所以现在事情变得有点更清楚了。
不过,你的这棵花树呢?好吧,您需要为自己编写一个折叠函数,该函数将采用两个值,其中一个与初始值和结果值相同,另一个是您的树的类型,并构建一个值结果类型的。如果我要使用伪代码以更具描述性的方式编写类型,我可能会编写如下内容:foldr :: (contentOfCollectionType -> resultType -> resultType) -> resultType -> (collectionWrapper contentOfCollectionType) -> resultType
但是你不必在这里使用foldr,事实上你不能使用它,除非你做一些花哨的类型类实例化的东西。您完全可以使用普通递归编写自己的折叠函数。这就是他们所追求的。
如果你想学习递归和折叠之类的,但你还不了解这些东西,我推荐我帮助编写的书。 http://happylearnhaskelltutorial.com 它更详细地解释了它,并提供了许多清晰的示例。如果您了解基础知识,应该很快就可以快速了解您想了解递归和折叠的点......但是如果您不了解,那么了解基础知识,因为您需要在了解其他内容之前了解它们。
我应该提到你的特定折叠也有一个转换功能。它是将Color 转换为x 的东西。您作为折叠函数使用的函数“将 x 压在一起”(即采用两个 x 值并产生另一个 x 值,与上面示例中的 (+) 非常相似)。它只能在树上工作,因为我们还为它提供了将 Color 转换为 x 的函数,这有效地将有意义的数据从树中取出,并将其放入折叠函数可以使用的形式中。
这里有一个非常漂亮的模式。
祝你好运!