Haskell 如何实现此折叠功能?

Haskell 如何实现此折叠功能?,haskell,tree,fold,catamorphism,Haskell,Tree,Fold,Catamorphism,给出了颜色和植物两种数据类型 data Color = Red | Pink | White | Blue | Purple | Green | Yellow deriving (Show, Eq) data Plant = Leaf | Blossom Color | Stalk Plant Plant deriving (Show, Eq) 现在,我应该实现以下类型的函数fold\u plant: (x -> x -> x) -> (C

给出了颜色和植物两种数据类型

data Color = Red | Pink | White | Blue | Purple | Green | Yellow
   deriving (Show, Eq)

data Plant =
     Leaf
   | Blossom Color
   | Stalk Plant Plant
   deriving (Show, Eq)
现在,我应该实现以下类型的函数
fold\u plant

(x -> x -> x) -> (Color -> x) -> x -> Plant -> x
我对折叠函数的理解是,它接受一个列表,每次迭代都会从列表中删除第一个元素,并对该元素进行处理

显然,折叠植物的茎、花、叶是植物的特征

现在我知道,在Haskell中,可以生成如下函数:

fold_plant :: (x -> x -> x) -> (Color -> x) -> x -> Plant -> x
fold_plant = do something

但从现在起,我不知道折叠函数在植物中是如何工作的。

如果我们看看函数签名:

fold_plant :: (x -> x -> x) -> (Color -> x) -> x -> Plant -> x
--            \_____ _____/    \____ _____/    |      |      |
--                  v               v          v      v      v
--                stalk           blossom     leaf  tree  output
我们看到有
部分以及
部分和
部分。我们将在这里命名
功能
s
功能
b
部分
l
。为了简化(和优化)函数,我们将解压这三个参数,然后调用递归方法:

现在的问题当然是如何处理
fold\u plant'
。如果我们看到一个
Leaf
,我们不需要对值执行任何操作,只需返回我们的Leaf结果
l

fold_plant' Leaf = l
如果我们发现一个
(c)
带有
c
a颜色,我们必须使用
b
部分执行从
c::color
x
的映射,以获得新值:

fold_plant' (Blossom c) = b c
最后,如果我们有一个
,我们将不得不执行递归:我们首先调用左茎上的
折叠植物
,然后调用
折叠植物
,并构造一个
s
,结果如下:

fold_plant' (Stalk lef rig) = s (fold_plant' lef) (fold_plant' rig)
因此,我们可以将其组合到以下函数中:

fold_plant s b l = fold_plant'
    where fold_plant' Leaf = l
          fold_plant' (Blossom c) = b c
          fold_plant' (Stalk lef rig) = s (fold_plant' lef) (fold_plant' rig)

折叠是一个函数,它获取结构中的一段数据,并将其折叠为另一段数据。通常我们这样做是为了将集合“减少”为单个值。这就是为什么如果你看看其他语言,比如Lisp、Smalltalk、Ruby、JavaScript等,你会发现这个叫做
reduce
的操作,它是Haskell中折叠的可怜表亲

我说这是可怜的表亲,因为你对列表的直觉是正确的,但在Haskell中,我们更抽象和一般,所以我们的折叠函数可以处理任何类型的结构,我们告诉Haskell折叠的含义

因此,我们可以讨论“使用加法和折叠将数字列表转换为求和值”,或者我们可以讨论“使用函数获取姓名的族谱并将其折叠为列表”,等等。任何时候,我们有这样的想法,将某个东西的结构改变为一个单一的值,或者可能改变为一组不同的结构化值,这就是折叠

在Haskell中,表示这一点的“规范”方法是
foldr::Foldable t=>(a->b->b->ta->b
,但它更容易,就像您所想的那样,在开始时使用“a列表”作为
Foldable f=>ta
类型,因为它更容易理解。所以我们有一种专门的
foldr::(a->b->b)->b->[a]->b
。但是什么是
a
b
?什么是
(a->b->b)
,这三个参数做什么

让我们专门为
a
b
设置
Int
值:
foldr::(Int->Int->Int)->Int->[Int]->Int
哇。。。那就是。。。很有趣,不是吗?所以
foldr
接受两个Int的函数,比如说
(+)
函数,它接受一个
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)->(minminnum-newNum,max-maxnum-newNum))(maxBound::Int,minBound::Int)

所以现在事情变得更清楚了

但是,你的花之树呢?你需要自己编写一个折叠函数,它包含两个值,其中一个值与初始值和结果值相同,另一个值与树的类型相同,并构建一个结果类型的值以更具描述性的方式,我可能会编写如下内容:
foldr::(contentOfCollectionType->resultType->resultType)->resultType->resultType->(collectionWrapper contentOfCollectionType)->resultType

但是你不必在这里使用
foldr
,事实上,除非你做了一些奇特的类型类实例化的东西,否则你不能使用它。你完全可以使用简单的递归编写你自己的折叠函数。这就是他们所追求的

如果你想了解复发
fold_plant s b l = fold_plant'
    where fold_plant' Leaf = l
          fold_plant' (Blossom c) = b c
          fold_plant' (Stalk lef rig) = s (fold_plant' lef) (fold_plant' rig)
    data Plant =                        data Result r =    
         Leaf                                RLeaf 
       | Blossom Color                     | RBlossom Color
       | Stalk Plant Plant                 | RStalk r r
            -- recursive data           -- non-recursive data: `r`, not `Result r`!
    -- single-step reduction semantics:
    -- reduction_step :: ..... -> Result r -> r
    reduction_step :: (r -> r -> r) -> (Color -> r) -> r -> Result r -> r
    reduction_step s b l  RLeaf       = l
    reduction_step s b l (RBlosom c)  = b c
    reduction_step s b l (RStalk x y) = s x y
    recurse_into :: (r -> r -> r) -> (Color -> r) -> r -> Plant -> Result r
    recurse_into s b l Leaf          = RLeaf
    recurse_into s b l (Blossom c)   = RBlossom c
    recurse_into s b l (Stalk lt rt) = RStalk (fold_plant s b l lt) (fold_plant s b l rt)
    fold_plant :: (r -> r -> r) -> (Color -> r) -> r -> Plant -> r
    fold_plant s b l plant = reduction_step s b l          --          Result r -> r
                               (recurse_into s b l plant)  -- Plant -> Result r