Обход дерева Haskell n-ary

Я довольно новичок в Haskell, и я пытаюсь понять, как пересечь дерево n-ary. В качестве вывода я ищу список листовых значений (поскольку ветви не имеют значения), поэтому для 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

любая помощь была бы очень признательна, поэтому заранее спасибо..!

3 ответов


обход дерева означает обход всех поддеревьев и сплющивание результирующих списков в один.

это переводится как

travTree (Branch branches) = concat $ map travTree branches

обратите внимание, что есть еще более краткие обозначения типа branches >>= travTree или concatMap travTree branches в правой части этого определения, но я считаю, что выше один, чтобы быть ясным.

Edit: повторное введение версии списка-понимания для полноты:

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

вы на правильном пути с map, но после прохождения каждого поддерева вы хотите concat получившиеся списки. Также нет смысла прерывать первый элемент списка с помощью (x:xs) шаблон при использовании map. Я бы написал это как:

travTree (Branch xs) = concatMap travTree xs

(но будьте осторожны, я не проверял это! Однако я часто нахожу, что мои проблемы "бесконечного типа a = [a]" вызваны map здесь это.)


когда я был новичком в 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

OK, вход определенно Tree a. Если выход должен быть [a], и первый оператор (:) :: a -> [a] -> [a], тогда нам нужно travTree x :: a и travTree xs :: [a]. Это работает?

Ну, это не удается по двум причинам: на самом деле, travTree x :: [a], и вы не можете минусы список в другой список (вам нужно (++) :: [a] -> [a] -> [a] для этого). И вы не можете пройти [Tree a] to 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].

другие ответы правы, говоря, что разрушение бессмысленно здесь, но я надеюсь, что видя типы прописанный помогает вам понять, как имитировать вывод типа в вашей голове. Таким образом, вы можете понять, что происходит неправильно для других, подобных проблем.