fold 是一个函数,它在结构中获取一段数据,并将其折叠为另一段数据。通常我们这样做是为了将集合“减少”为单个值。这就是为什么如果您查看其他语言,如 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
值(这是它将用作目标结果的初始值,以及一个Int
值列表......然后它将产生一个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它更详细地解释了它,并提供了许多清晰的示例。如果您了解基础知识,应该很快就可以快速了解您想了解递归和折叠的点......但是如果您不了解,那么了解基础知识,因为您需要在了解其他内容之前了解它们。
我应该提到你的特定折叠也有一个转换功能。它是将 a 转换为 的Color
东西x
。您被赋予作为折叠函数使用的函数“将 x 压在一起”(即采用两个x
值并产生另一个x
值,与我们上面的示例非常相似(+)
)。它只能在树上工作,因为我们也给它这个函数来把 aColor
变成 a x
,这有效地从树中取出有意义的数据并将其放入折叠函数可以使用的形式。
这里有一个非常漂亮的模式。
祝你好运!