Обход дерева 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]
.
другие ответы правы, говоря, что разрушение бессмысленно здесь, но я надеюсь, что видя типы прописанный помогает вам понять, как имитировать вывод типа в вашей голове. Таким образом, вы можете понять, что происходит неправильно для других, подобных проблем.