【问题标题】:Counting number of Nodes in tree Haskell计算树 Haskell 中的节点数
【发布时间】:2019-02-23 15:36:18
【问题描述】:

我是 Haskell 的新手,对某些语法部分(来自 C/C++)仍然有些困惑。

我有这个 Tree 数据类型(如下)和 t2 的构造函数

data Tree a b = Leaf a                                                          
              | Node b (Tree a b) (Tree a b)                                    
     deriving (Eq, Show)

 t2 :: Tree Bool Int                                                             
 t2 = Node 17 (Node 2 (Leaf True)                                                
                      (Node 7 (Leaf False)                                       
                              (Leaf False)))                                     
              (Leaf True)

我需要编写一个函数来计算树的节点数

 sizeTree :: Tree a b -> Int

我很困惑为什么 sizeTree 会为函数传入“a”和“b”。是因为“a”有一个叶子,“b”有一个节点吗?我知道这需要递归调用,但我应该从哪里开始?

【问题讨论】:

  • a b 是类型,例如 Int Char 等。它们被传递给 Tree 而不是 sizetree Think c±+ 模板类
  • 如果你是 Haskell 的新手,那么我会从一个更简单的树开始,比如 data Tree a = Empty | Node a (Tree a) (Tree a) 并忘记派生。现在实现函数size :: Tree a -> Int。没有必要将其命名为 sizeTree,因为从您的签名的其余部分可以看出它是用于树木的。
  • sizeTree 不接受三个参数Treeab,而只接受一个(Tree a b)。后者类似于 C++ 模板Tree<a,b>。那将是一棵树,其叶子带有 a 类型的值,而内部节点带有 b 类型的值。

标签: haskell


【解决方案1】:

一些 cmets 已经解决了您对类型签名的困惑,但我会尝试另一种方式来查看,比评论允许的更详细。

我们可以比较一下这个类型签名:

sizeTree :: Tree a b -> Int

对于列表中的函数,比如length 函数:

length :: [a] -> Int 

这个函数不“接受a”作为参数,它接受[a],它代表一个列表,其元素类型为a。关键是a 可以代表any 类型——事实上,将其明确写为forall a. [a] -> Int 是合法的,这清楚地表明该函数适用于any 列表类型。也就是说,一个 length 函数可以应用于 Ints 列表,或 Chars 列表(即字符串),或 DoubleInteger 的对列表的列表s - 希望你能明白。您可能认为这是理所当然的,您可以调用 length [1,2,3]length "hello" 并让它们都工作 - 但是,鉴于 Haskell 强大的类型系统,这仅适用于多态类型。

Tree 的情况完全相同。这实际上是一个“类型族”,就像列表一样——它本身不是一个类型,没有Tree 类型的值。但是有Tree Int CharTree String (Int, Double) 类型的值,以及您能想到的任何其他使用两个“具体类型”来代表ab。通过声明一个函数sizeTree :: Tree a b -> Int - 明确地sizeTree :: forall a. forall b. Tree a b -> Int - 您可以确保它适用于这些无限多不同的具体类型中的任何一个。

至于编写函数本身,还是类似于列表。列表类型虽然是 Haskell 内置的,但本质上具有两个构造函数,一个用于空列表,一个用于接收元素和现有列表(您可以自己定义等效类型为 data List a = Null | Cons a (List a) - 这意味着您可以在这两个构造函数,要做:

length Null = 0
length (Cons _ l) = 1 + length l

或者,为 Haskell 的原生列表类型使用内置的“语法糖”:

length [] = 0
length (_:l) = 1 + length l

(如果您对_ 感到困惑,它代表适当类型的任何值,这意味着在这种情况下我们不关心它的值,如果您愿意,我可以给它起任何名字' d 首选,比如x,它的意思完全一样。)

希望这是足够的背景知识来帮助您了解正在发生的事情,并填写@Rusi 给您的开始 - 因为这是您的 Tree a b 类型的两个构造函数,所以您需要在两者上定义函数这些。进一步提示:Node 构造函数将涉及对sizeTree 的递归调用,就像length 定义的第二行也使用递归调用一样。

还有什么问题,欢迎提问。

【讨论】:

  • 非常感谢,这真的解释了一切!
  • 很高兴为您提供帮助,请考虑通过单击对勾图标来投票和/或“接受”它。
【解决方案2】:

这样开始

sizetree (Leaf x)  = ???
sizetree (Node y left right) = ???

填写???

【讨论】:

  • 你可以写undefined,而不是写???,这甚至是合法的Haskell并且可以编译。
  • 同样,您可以使用error "haven't done this yet"(或您选择的任何其他字符串)使错误消息(在运行时,仍然可以正常编译)更加明确。
  • 或者使用typed hole,它会给你错误信息,解释你需要用什么类型来填充它们以及手头有什么类型。 sizetree (Leaf x) = _base; sizetree (Node y left right) = _recursive。也就是说,问号是 SO 答案中很好的填充物,我一直都在使用它们。
猜你喜欢
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
  • 2013-02-12
  • 1970-01-01
  • 2016-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多