如何用这种折叠函数计算植物叶片的数量?

时间:2017-07-10 22:43:55

标签: haskell tree fold

Color和Plant是数据类型,定义如下:

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

data Plant=
      Leaf
   |  Blossom Color
   |  Stalk Plant Plant
   deriving (Show, Eq)

折叠功能是:

fold_plant :: (x -> x -> x) -> (Color -> x) -> x -> Plant -> x
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' right)

现在我应该实现一个函数amount,它使用fold函数,以Plant作为参数并返回工厂中的叶子数量。 我认为我现在面临的主要问题是我应该使用的折叠功能不像通常的折叠功能那样工作。我的意思是foldrfoldl,因为它们有3个参数:一个函数,一个初始值和一个列表。但是在我的函数fold_plant中,没有一个。

这是我想到的,但我认为我并不理解所有事情。

amount :: Plant-> Integer
amount input = length . filter (Leaf ==) fold_plant Stalk Blossom Leaf input

2 个答案:

答案 0 :(得分:4)

另一个答案直接跳到解决方案,然后将该解决方案视为正确的解决方案。在这个答案中,我想转向另一个方向:描述一种方法,你可以自己发明解决方案。

我会假设您可以自己首先编写此函数而不用折叠,只需使用基本模式匹配和递归。以下是您可能提出的建议(可能是模数命名):

amount Leaf = 1
amount (Blossom c) = 0
amount (Stalk lef rig) = amount lef + amount rig

现在与折叠的简化定义(它没有使用where - 块辅助函数)进行比较:

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

我的天哪这两个定义看起来很相似!我们假设某amount = fold_plant s b lsb的某些选择l。看看前两个方程:

amount           Leaf = 1
fold_plant s b l Leaf = l

所以我们应该选择l = 1。对于后两个方程:

amount           (Blossom c) = 0
fold_plant s b l (Blossom c) = b c

所以我们应该选择b c = 0。最后两个方程式:

amount           (Stalk lef rig) = amount lef + amount rig
fold_plant s b l (Stalk lef rig) = s (fold_plant s b l lef) (fold_plant s b l rig)

好吧,回想一下我们的假设是amount = fold_plant s b l,所以

s (fold_plant s b l lef) (fold_plant s b l rig) = s (amount lef) (amount rig)

现在有两个方程式:

amount           (Stalk lef rig) = amount lef + amount rig
fold_plant s b l (Stalk lef rig) = s (amount lef) (amount rig)

所以我们应该选择s x y = x + y。一下子写下这些方程式,我们得出了答案:

amount = fold_plant s b l where
    l = 1
    b c = 0
    s x y = x + y

答案 1 :(得分:3)

不,通常使用fold_plant 作为计算叶数的方法。你可以这样做:

amount :: Num n => Plant -> n
amount = fold_plant (+) (const 0) 1

这意味着:

  • 对于每个Stalk x y,我们将其折叠并返回两个孩子的叶数的总和;
  • 对于每个Blossom x,我们都会返回0(因为我们在const 0颜色上调用x,而const 0每个都会返回0输入);和
  • 对于每个Leaf,我们都会返回1

因此,由于每个假期都映射到1Blossom上的每个0以及Stalk作为附加(+)运算符,我们都会获得叶子总数。