我是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”的节点?我知道这需要递归调用,但是我应该从哪里开始?
答案 0 :(得分:3)
多个注释已经解决了您对类型签名的困惑,但是我将尝试另一种查看方式,其细节比注释所允许的多。
我们可以比较这种类型的签名:
sizeTree :: Tree a b -> Int
与列表中的函数相同,请说length
函数:
length :: [a] -> Int
此函数不“使用a
作为参数,而是使用[a]
,它表示元素类型为a
的列表。至关重要的是a
可以代表 any 类型-明确地将其写为forall a. [a] -> Int
是合法的,这清楚地表明该函数可以在{{1}上运行}列表类型。也就是说,一个any
函数可以应用于整数列表,字符列表(即字符串)或成对列表length
和列表的Double
中-希望您能想到。您可能认为这是理所当然的,您可以调用Integer
和length [1,2,3]
并使它们都起作用-但是,考虑到Haskell强大的类型系统,它只能工作于多态类型。
与length "hello"
完全相同。这实际上是一个“类型族”,就像列表一样-它本身不是类型,没有类型Tree
的值。但是存在类型Tree
或Tree Int Char
的值,您可以想到的任何其他值都使用两个“具体类型”来代表Tree String (Int, Double)
和a
。通过声明一个函数b
-显式sizeTree :: Tree a b -> Int
-您可以确保该函数可用于无限多种具体类型中的任何一种。
关于编写函数本身,再次类似于列表。列表类型虽然是Haskell内置的,但实际上具有两个构造函数,一个用于空列表,一个用于接收元素和一个现有列表(您可以将自己的等效类型定义为sizeTree :: forall a. forall b. Tree a b -> Int
-这意味着您可以进行模式化在这两个构造函数上进行匹配,即可:
data List a = Null | Cons a (List a)
,或者对Haskell的本机列表类型使用内置的“语法糖”:
length Null = 0
length (Cons _ l) = 1 + length l
(如果您对length [] = 0
length (_:l) = 1 + length l
感到困惑,它代表任何适当类型的值,这意味着在这种情况下,我们不在乎它的值,如果可以您会更愿意说_
,那将意味着完全一样的事情。)
希望这是足够的背景知识,可以帮助您了解正在发生的事情,并填写@Rusi给您的开头-因为它们是您x
类型的两个构造函数,因此您需要在这两个。进一步的提示:Tree a b
构造函数将涉及对Node
的递归调用,就像sizeTree
定义的第二行也使用递归调用一样。
还有其他问题,请随时提问。
答案 1 :(得分:1)
像这样开始
sizetree (Leaf x) = ???
sizetree (Node y left right) = ???
填写???