Каково правильное определение `развернуть` для нетегированного дерева?

Я думал о том, как реализовать эквивалент unfold для следующего типа:

data Tree a = Node (Tree a) (Tree a) | Leaf a | Nil

Это было не сразу очевидно, так как стандарт unfold для списков возвращает значение и следующее начальное число. Для этого типа данных это не имеет смысла, так как нет никакого "значения", пока вы не достигнете конечного узла. Таким образом, имеет смысл возвращать новые семена или останавливаться на значении. Я использую это определение:

data Drive s a = Stop | Unit a | Branch s s deriving Show

unfold :: (t -> Drive t a) -> t -> Tree a
unfold fn x = case fn x of
    Branch a b -> Node (unfold fn a) (unfold fn b)
    Unit a     -> Leaf a
    Stop       -> Nil

main = print $ unfold go 5 where
    go 0 = Stop
    go 1 = Unit 1
    go n = Branch (n - 1) (n - 2)

Хотя это, кажется, работает, я не уверен, что так и должно быть. Итак, вот в чем вопрос: как правильно это сделать?

1 ответ

Решение

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

module Unfold where

Здесь мы начинаем по определению точку фиксации функтора f: это слой f затем следует еще одна точка фиксации:

newtype Fix f = InFix { outFix :: f (Fix f) }

Чтобы сделать вещи немного яснее, вот определения функторов, соответствующих спискам и деревьям. Они имеют в основном ту же форму, что и типы данных, за исключением того, что мы должны заменить рекурсивные вызовы дополнительным параметром. Другими словами, они описывают, как выглядит один слой списка / дерева, и являются общими для возможных подструктур. r,

data ListF a r = LNil | LCons a r
data TreeF a r = TNil | TLeaf a | TBranch r r

Списки и деревья являются соответственно точками фиксирования ListF и TreeF:

type List a = Fix (ListF a)
type Tree a = Fix (TreeF a)

В любом случае, теперь у вас есть лучшая интуиция в этом бизнесе с фиксированной точкой, мы видим, что существует общий способ определения unfold функция для них.

Учитывая оригинальное семя, а также функцию взятия семени и создания одного слоя f где в рекурсивной структуре появляются новые семена, мы можем построить целую структуру:

unfoldFix :: Functor f => (s -> f s) -> s -> Fix f
unfoldFix node = go
  where go = InFix . fmap go . node

Это определение специализируется на обычном unfold в списке или ваше определение для деревьев. Другими словами: ваше определение действительно было правильным.

Другие вопросы по тегам