我对Haskell很新,我正在尝试研究如何遍历一棵n-ary树。作为输出,我希望得到一个Leaf值列表(因为分支没有值),所以对于testtree,这将是:4,5
到目前为止我的定义是:
data Tree a = Leaf a | Branch [Tree a] deriving (Show)
travTree :: Tree a -> [a]
travTree (Leaf x) = [x]
travTree (Branch (x:xs)) = travTree x : travTree xs
testtree = Branch [(Leaf "4"), (Leaf "5")]
但它给出了错误:
Couldn't match expected type `Tree a'
against inferred type `[Tree a]'
In the first argument of `travTree', namely `xs'
In the second argument of `(:)', namely `travTree xs'
In the expression: travTree x : travTree xs
我假设这是因为xs是一个树的列表,并且它期望一棵奇异的树。有没有办法做到这一点?我一直在尝试使用map函数:
travTree (Branch (x:xs)) = travTree x : map travTree xs
但它抱怨说:
Occurs check: cannot construct the infinite type: a = [a]
When generalising the type(s) for `travTree'
我也尝试将功能签名更改为:
travTree :: Tree a -> [b]
出现错误:
Couldn't match expected type `a' against inferred type `[b]'
`a' is a rigid type variable bound by
the type signature for `travTree' at Main.hs:149:36
In the first argument of `(:)', namely `travTree x'
In the expression: travTree x : map travTree xs
In the definition of `travTree':
travTree (Branch (x : xs)) = travTree x : map travTree xs
非常感谢任何帮助,所以提前感谢..!
答案 0 :(得分:10)
您使用map
位于右侧,但在遍历每个子树后,您希望concat
生成的列表在一起。使用(x:xs)
时,使用map
模式切断列表的第一个元素也毫无意义。我把它写成:
travTree (Branch xs) = concatMap travTree xs
(但要注意;我没有测试过!但是我经常发现我的“无限类型a = [a]”问题是由需要map
的{{1}}引起的。)
答案 1 :(得分:8)
遍历树意味着遍历所有子树并将生成的列表展平为一个。
这转换为
travTree (Branch branches) = concat $ map travTree branches
请注意,此定义的右侧有更简洁的符号,例如branches >>= travTree
或concatMap travTree branches
,但我认为上面的符号是最清晰的。
编辑:为了完整起见,重新引入列表理解版本:
travTree (Branch branches) = [ elem | br <- branches, elem <- travTree br ]
答案 2 :(得分:7)
当我刚接触Haskell时,我遇到了同样的问题。我终于找到了如何通过放慢速度并查看类型来解决问题。 (当我写了很多Scheme时,我反而放慢速度,看看非常简单的输入/输出对。我有时会在Haskell中这样做,但直到我查看了类型。)
travTree :: Tree a -> [a]
travTree (Leaf x) = [x]
travTree (Branch (x:xs)) = travTree x : travTree xs
你的类型看起来是正确的:Tree a -> [a]
听起来像是“所有的叶子”。
travTree (Leaf x) = [x]
此案例正确地将Tree a
转换为[a]
。
travTree (Branch (x:xs)) = travTree x : travTree xs
好的,输入肯定是Tree a
。如果输出为[a]
,第一个运算符为(:) :: a -> [a] -> [a]
,则我们需要travTree x :: a
和travTree xs :: [a]
。这有用吗?
好吧,它失败有两个原因:实际上,travTree x :: [a]
,你不能将列表排在另一个列表上(你需要(++) :: [a] -> [a] -> [a]
)。并且您无法将[Tree a]
传递给travTree :: Tree a -> [a]
- 当您需要一棵树时,您会给它一个树列表。
您可以使用map
:map travTree xs
解决第二个问题。它的类型为[Tree a] -> [[a]]
。幸运的是,这现在适合travTree x :
,所以
(travTree x : map travTree xs) :: [[a]]
现在您遇到的问题是[[a]]
而不是[a]
。 concat
通过展平一次来解决这个问题,所以
travTree (Branch (x:xs)) = concat (travTree x : map travTree xs) :: [a]
与预期的Tree a -> [a]
匹配。
其他答案恰到好处地说,这里的解构是没有意义的,但我希望看到拼出的类型可以帮助你理解如何模仿你头脑中的类型推理。这样你就可以解决其他类似问题出了什么问题。