Haskell n-ary tree traversal

前端 未结 3 1607
一向
一向 2021-02-04 14:51

I\'m pretty new to Haskell and I\'m trying to work out how to traverse a n-ary tree. As output I\'m looking to get a list of Leaf values (as the branches have no value), so for

相关标签:
3条回答
  • 2021-02-04 15:08

    You're on the right lines with map, but after traversing each subtree you want to concat the resulting lists together. There's also no point breaking off the first element of the list with the (x:xs) pattern when using map. 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.)

    0 讨论(0)
  • 2021-02-04 15:23

    When I was new to Haskell I ran into the same problem a lot. 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.)

    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.

    travTree (Leaf x) = [x]
    

    This case properly converts a Tree a to a [a].

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

    OK, the input is definitely 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]. 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). 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.

    You can address the second problem by using map: map travTree xs. This has the type [Tree a] -> [[a]]. Fortunately, this now fits the travTree x :, so that

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

    Now you just have the problem that you have [[a]] instead of [a]. concat solves this problem by flattening once, so

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

    which matches the expected 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.

    0 讨论(0)
  • 2021-02-04 15:30

    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.

    Edit: Reintroducing the list-comprehension version for the sake of completeness:

    travTree (Branch branches) = [ elem | br <- branches, elem <- travTree br ]
    
    0 讨论(0)
提交回复
热议问题