简体   繁体   English

树上的Haskell折叠功能

[英]Haskell fold function on tree

I'm struggling with this example: 我正在努力与这个例子:

Write a fold function that folds a function over a tree. 编写一个将函数折叠到树上的折叠函数。 (Thus, for example, fold min t would find the smallest element in tree.) (因此,例如, fold min t将在树中找到最小的元素。)

 fold :: (a -> a -> a) -> Tree -> a 

*That's my script *这是我的剧本

data Tree = Leaf Int
          | Fork Tree Int Tree
     deriving Show


t0 = Leaf 0
t1 = Fork (Fork (Leaf 1) 2 (Leaf 3)) 4 (Fork (Leaf 5) 6 (Leaf 7))
t2 = Fork (Fork (Fork (Leaf 1) 2 (Leaf 3)) 4 (Leaf 5)) 6 (Leaf 7)

fold :: (a -> a -> a) -> Tree -> a
fold f v (Leaf n) = v
fold f v (Fork l n r) = f (Folk l) (fold f v (Fork r))

Thanks for any advice 感谢您的任何建议

One thing, in the signature of your function fold , you're saying that it'll receive 2 arguments, a binary function (could be an operator), a Tree and returns an a . 有一件事,在函数fold的签名中,您要说它将接收2个参数,一个二进制函数(可以是一个运算符),一个Tree并返回a Now, in the defintion you have 3 arguments, f , some v and a constructor of Tree . 现在,在定义中,您有3个参数f ,一些vTree的构造函数。

The solution could be this one: 解决方案可能是这样的:

fold :: (Int -> Int -> Int) -> Tree -> Int
fold f (Leaf n) = n
fold f (Fork l n r) = f n m
    where
        m = f (fold f l) (fold f r)

You need to say that you are returning a Int because your Tree holds Int . 您需要说您正在返回一个Int因为您的Tree拥有Int

EDIT 编辑

Furthermore, you can redefine your data with a rigid type variable. 此外,您可以使用刚性类型变量来重新定义数据。

data Tree a = Leaf a
            | Fork (Tree a) a (Tree a)
     deriving Show


fold :: (a -> a -> a) -> Tree a -> a
fold f (Leaf n) = n
fold f (Fork l n r) = f n m
    where
        m = f (fold f l) (fold f r)

I suggest you to read the documentation of Data.Foldable . 我建议您阅读Data.Foldable的文档。 They have an example with a Tree data type. 他们有一个使用Tree数据类型的示例。

Folds can be somewhat mechanically derived from any type. 折痕可以机械地衍生自任何类型。 Here's foldMaybe : 这是foldMaybe

data Maybe a = Nothing | Just a

foldMaybe :: r -> (a -> r) -> Maybe a -> r
foldMaybe _ k (Just a) = k a
foldMaybe d _ Nothing  = d

To "fold" a data type means that we are going to provide a way of combining each of the possible constructors for that type into a final type r . “折叠”数据类型意味着我们将提供一种将该类型的每种可能的构造函数组合为最终类型r Here, Maybe has two constructors: one with 0 values, and one with a single value of type a . 在这里, Maybe有两个构造函数:一个构造函数具有0个值,另一个构造函数具有单个a类型a值。 So, to fold it, we need to provide a value for the Nothing case, and a function to convert the Just a into an r . 因此,要折叠它,我们需要为Nothing情况提供一个值,并提供一个将Just a转换为r

Let's look at list , and see how foldr is derived from it. 让我们看一下list ,看看foldr是如何从中派生的。

data List a = Nil | Cons a (List a)

           [1]   [2    3    4]             [4]
foldList :: r -> (a -> r -> r) -> List a -> r
  1. This is the value to use when replacing the Nil in the list. 这是替换列表中的Nil时要使用的值。
  2. This is the function to use for combining the Cons case. 这是用于结合Cons情况的功能。 Since Cons has an a as it's first value, the function must take an a . 由于Cons的第一个值是a ,因此该函数必须取a
  3. The second value that Cons takes is a (List a) , so why are we taking an r ? Cons取的第二个值是a (List a) ,为什么我们取r Well! 好! We're defining a function that can take a List a and return an r , so we'll process the rest of the list before combining with the current node. 我们正在定义一个可以使用List a并返回r ,因此我们将在与当前节点合并之前处理列表的其余部分。
  4. The fold function returns r . fold函数返回r

What does the implementation look like? 实现是什么样的?

foldList nil cons list = case list of
    Nil -> nil
    Cons a as -> cons a (foldList nil cons list)

So we replace all of the constructors with their "destructor" counterparts, recursing where necessary. 因此,我们将所有构造函数替换为对应的“析构函数”,并在必要时重复进行。

For trees: 对于树木:

data Tree = Leaf Int | Fork Tree Int Tree

We have two constructors: one takes an Int , and the other takes three parameters Tree , Int , and Tree . 我们有两个构造函数:一个构造函数使用一个Int ,另一个构造函数使用三个参数TreeIntTree

Let us write the type signature! 让我们来写类型签名!

treeFold :: LeafCase r -> ForkCase r -> r

Ah, but we have to define LeafCase : 嗯,但是我们必须定义LeafCase

type LeafCase r = Int -> r

Since Leaf takes a single Int as parameter, that's what the type of this function must be. 由于Leaf使用单个Int作为参数,因此这就是该函数的类型。

type ForkCase r = r -> Int -> r -> r

Likewise, we replace all the recursive uses of Tree in Fork with the r type parameter. 同样,我们用r类型参数替换了ForkTree所有递归用法。 So our full function looks like: 因此,我们的完整功能如下所示:

foldTree :: (Int -> r) -> (r -> Int -> r -> r) -> Tree -> r

The implementation might start like: 实施可能会像这样开始:

foldTree leaf fork tree = case tree of
    Leaf i -> error "halp"
    Fork l i r -> error "complete me?"

and I feel confident that you can fill in those holes :) 我有信心您可以填补这些空缺:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM