简体   繁体   English

Haskell n-ary树遍历

[英]Haskell n-ary tree traversal

I'm pretty new to Haskell and I'm trying to work out how to traverse a n-ary tree. 我对Haskell很新,我正在努力研究如何遍历一棵n-ary树。 As output I'm looking to get a list of Leaf values (as the branches have no value), so for testtree this would be: 4,5 作为输出,我希望获得Leaf值列表(因为分支没有值),因此对于testtree,这将是:4,5

My definition so far is: 到目前为止我的定义是:

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")]

But it gives the error: 但它给出了错误:

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

I'm assuming this is due to xs being a list of trees and its expecting a singular tree. 我假设这是因为xs是一个树列表,它期待一棵奇异的树。 Is there a way to do this? 有没有办法做到这一点? I've been trying the map function, along the lines of: 我一直在尝试地图功能,顺序如下:

travTree (Branch (x:xs))    = travTree x : map travTree xs

But it then complains of: 但它抱怨说:

Occurs check: cannot construct the infinite type: a = [a]
When generalising the type(s) for `travTree'

I've also tried changing the function signature to: 我也尝试将函数签名更改为:

travTree                    :: Tree a -> [b]

Which gives the error: 这给出了错误:

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

Any help would be greatly appreciated, so thanks in advance..! 任何帮助将不胜感激,所以提前感谢..!

You're on the right lines with map , but after traversing each subtree you want to concat the resulting lists together. 你在右边线与map ,但遍历每个子树后要concat所产生的名单一起。 There's also no point breaking off the first element of the list with the (x:xs) pattern when using map . 使用map时,使用(x:xs)模式切断列表的第一个元素也没有意义。 I'd write this as: 我写这个:

travTree (Branch xs) = concatMap travTree xs

(But beware; I haven't tested that! However I often find my "infinite type a = [a]" problems are caused by a map where concatMap is needed.) (但要注意;我没有测试过!但是我经常发现我的“无限类型a = [a]”问题是由需要concatMapmap引起的。)

Traversing a tree means traversing all subtrees and flattening the resulting lists into one. 遍历树意味着遍历所有子树并将生成的列表展平为一个。

This translates to 这转化为

travTree (Branch branches) = concat $ map travTree branches

Note that there are even more concise notations like branches >>= travTree or concatMap travTree branches for the right hand side of this definition, but I consider the above one to be the clearest. 请注意,对于此定义的右侧,还有更简洁的符号,例如branches >>= travTreeconcatMap travTree branches ,但我认为上面的concatMap travTree branches是最清晰的。

Edit: Reintroducing the list-comprehension version for the sake of completeness: 编辑:为了完整性重新引入列表理解版本:

travTree (Branch branches) = [ elem | br <- branches, elem <- travTree br ]

When I was new to Haskell I ran into the same problem a lot. 当我刚接触Haskell时,我遇到了同样的问题。 I finally figured out how to solve the problem by slowing down and looking at the types. 我终于找到了如何通过放慢速度并查看类型来解决问题。 (Back when I wrote a lot of Scheme, I instead slowed down and look at very simple input/output pairs. I do that sometimes in Haskell, but not until I've looked at the types.) (当我写了很多Scheme时,我反而放慢速度并查看非常简单的输入/输出对。我有时会在Haskell中这样做,但直到我查看了类型。)

travTree                    :: Tree a -> [a]
travTree (Leaf x)           = [x]
travTree (Branch (x:xs))    = travTree x : travTree xs

Your type looks right: Tree a -> [a] sounds like "all the leaves" to me. 你的类型看起来正确: Tree a -> [a]听起来像“所有的叶子”给我。

travTree (Leaf x) = [x]

This case properly converts a Tree a to a [a] . 这种情况适当地将Tree a转换为[a]

travTree (Branch (x:xs)) = travTree x : travTree xs

OK, the input is definitely a Tree a . 好的,输入绝对是Tree a If the output is to be [a] , and the first operator is (:) :: a -> [a] -> [a] , then we need travTree x :: a and travTree xs :: [a] . 如果输出是[a] ,并且第一个运算符是(:) :: a -> [a] -> [a] ,那么我们需要travTree x :: atravTree xs :: [a] Does this work? 这有用吗?

Well, it fails for two reasons: actually, travTree x :: [a] , and you can't cons a list onto another list (you need (++) :: [a] -> [a] -> [a] for that). 好吧,它失败有两个原因:实际上, travTree x :: [a] ,你不能将列表放到另一个列表上(你需要(++) :: [a] -> [a] -> [a]对于这一点)。 And you can't pass [Tree a] to travTree :: Tree a -> [a] --you're giving it a list of trees when it expects a single tree. 并且你不能将[Tree a]传递给travTree :: Tree a -> [a]当你需要一棵树时你给它一个树列表。

You can address the second problem by using map : map travTree xs . 您可以使用map来解决第二个问题: map travTree xs This has the type [Tree a] -> [[a]] . 它的类型为[Tree a] -> [[a]] Fortunately, this now fits the travTree x : , so that 幸运的是,这现在适合travTree x :所以

(travTree x : map travTree xs) :: [[a]]

Now you just have the problem that you have [[a]] instead of [a] . 现在你遇到的问题是[[a]]而不是[a] concat solves this problem by flattening once, so concat通过展平一次来解决这个问题,所以

travTree (Branch (x:xs)) = concat (travTree x : map travTree xs) :: [a]

which matches the expected Tree a -> [a] . 它匹配预期的Tree a -> [a]

The other answers are right in saying that the destructuring is pointless here, but I hope that seeing the types spelled out helps you understand how to mimic the type inference in your head. 其他答案是正确的说,这里的解构是没有意义的,但我希望看到拼出的类型有助于你理解如何模仿你头脑中的类型推断。 That way you can work out what's going wrong for other, similar problems. 这样你就可以解决其他类似问题的问题。

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

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