【问题标题】:Haskell n-ary tree traversalHaskell n-ary 树遍历
【发布时间】:2010-02-25 16:27:03
【问题描述】:

我对 Haskell 很陌生,我正在尝试找出如何遍历 n 叉树。作为输出,我希望得到一个叶子值列表(因为分支没有值),所以对于 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 是一个树列表并且它期望一个奇异树。有没有办法做到这一点?我一直在尝试地图功能,大致如下:

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

任何帮助将不胜感激,所以在此先感谢..!

【问题讨论】:

    标签: haskell functional-programming tree


    【解决方案1】:

    你在map 的正确行上,但是在遍历每个子树之后,你希望concat 将结果列表放在一起。当使用map 时,也没有必要用(x:xs) 模式断开列表的第一个元素。我会这样写:

    travTree (Branch xs) = concatMap travTree xs
    

    (但请注意;我还没有测试过!但是我经常发现我的“无限类型 a = [a]”问题是由需要 concatMapmap 引起的。)

    【讨论】:

    • 还有:一个 (:) 需要一个 (++)。
    • 谢谢!我希望这很简单,很高兴我离得太远了:-)
    【解决方案2】:

    遍历一棵树意味着遍历所有子树并将结果列表扁平化为一个。

    这转化为

    travTree (Branch branches) = concat $ map travTree branches
    

    请注意,此定义的右侧还有更简洁的符号,例如 branches >>= travTreeconcatMap travTree branches,但我认为上面的符号是最清晰的。

    编辑:为了完整起见重新引入列表理解版本:

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

    【讨论】:

    • 您对列表理解的第一个回答非常好...但很高兴看到您也同意我的回答!
    • 你也可以使用 concatMap ;)
    • 我喜欢这个解决方案,因为它有点分解。我同意,它比 concatMap 函数更清晰。我也喜欢列表理解(虽然最初理解起来有点复杂),所以再次感谢! :-)
    【解决方案3】:

    当我刚接触 Haskell 时,我经常遇到同样的问题。我终于想出了如何通过放慢速度并查看类型来解决问题。 (当我写了很多 Scheme 时,我反而放慢了速度并查看了非常简单的输入/输出对。我有时在 Haskell 中这样做,但直到我查看了类型。)

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

    您的类型看起来正确:Tree a -&gt; [a] 对我来说听起来像是“所有的叶子”。

    travTree (Leaf x) = [x]
    

    本例正确地将Tree a 转换为[a]

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

    好的,输入肯定是Tree a。如果输出是[a],第一个运算符是(:) :: a -&gt; [a] -&gt; [a],那么我们需要travTree x :: atravTree xs :: [a]。这行得通吗?

    嗯,它失败的原因有两个:实际上,travTree x :: [a],你不能将一个列表放到另一个列表上(你需要(++) :: [a] -&gt; [a] -&gt; [a])。而且你不能将[Tree a] 传递给travTree :: Tree a -&gt; [a]——当它需要一棵树时,你会给它一个树列表。

    您可以使用mapmap travTree xs 来解决第二个问题。它的类型为[Tree a] -&gt; [[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 -&gt; [a]匹配。

    其他答案是正确的说解构在这里毫无意义,但我希望看到拼写出来的类型可以帮助您理解如何在脑海中模仿类型推断。这样您就可以找出其他类似问题的问题所在。

    【讨论】:

    • 非常感谢,这真的很清楚,我真的很感激!读完这篇文章后,其他解决方案就更有意义了。
    • 这很好。那些“无法构造无限类型:a = [a]”的错误一开始就非常令人困惑,而您的回答很好,很清楚这是从哪里来的。
    猜你喜欢
    • 2017-11-28
    • 2014-04-18
    • 1970-01-01
    • 1970-01-01
    • 2014-01-10
    • 2018-01-21
    • 2014-09-26
    • 2021-11-20
    • 1970-01-01
    相关资源
    最近更新 更多