Полагаю, это домашнее задание. Предполагая, что это так, вот несколько идей о том, как подумать о проблеме, которая может привести вас к ответу:
В preorder
сначала «сообщается» текущий элемент, а затем повторяется для всех хвостов этого узла. В postorder
эти два шага выполняются в обратном порядке. В обоих случаях рекурсия является «локальной», в том смысле, что ей нужно иметь дело только с одним узлом за раз. Это правда для levelorder
? Или спросить это по-другому, когда levelorder
рекурсанты, результаты рекурсий взаимодействуют или нет? Если так, то что такое «единица» рекурсии, если не один Tree
?
Понимание природы рекурсии (или итерации ...?) levelorder
приведет вас к очень простому и элегантному решению. Моя версия длиной всего три строки!
Кстати, было бы неплохо иметь эти служебные функции, чтобы в некоторых местах код был еще понятнее:
element :: Tree a -> a
element (Nd x _) = x
subtrees :: Tree a -> [Tree a]
subtrees (Nd _ s) = s
Или, если вы знакомы с синтаксисом записей в Haskell, вы можете добиться именно этого, изменив исходное определение Tree
на:
data Tree a = Nd { element :: a, subtrees :: [Tree a] }
deriving Show
Полное решение:
Ключ понимает, что levelorder
требует рекурсии в списке Tree
. На каждом шаге извлекаются элементы из каждого Tree
, а следующий шаг происходит после объединения поддеревьев:
levelorder :: Tree a -> [a]
levelorder t = step [t]
where step [] = []
step ts = map element ts ++ step (concatMap subtrees ts)
Это создает элементы в одном сплюснутом списке, очень похожем на preorder
и postorder
do, и является обычным определением обхода в ширину.
Если вместо этого вы действительно хотите сгруппировать элементы по уровню, то одно изменение оператора ++
на :
приведет к этой версии:
bylevel :: Tree a -> [[a]]
bylevel t = step [t]
where step [] = []
step ts = map element ts : step (concatMap subtrees ts)
Примечание: я дал сигнатуры типов для всех функций верхнего уровня. Это действительно хорошая привычка, и вы сэкономите много времени на отладке.