Я думаю, что лучший способ выполнить sh то, что вы хотите здесь сделать, - это обход (в смысле класса Traversable
). Во-первых, я собираюсь немного обобщить розовые деревья:
data Tree a
= a :& [Tree a]
deriving (Show, Eq, Ord, Functor, Foldable, Traversable)
Все функции, которые я показываю, должно быть довольно просто преобразовать в определение дерева, которое вы дали, но этот тип немного более общий и, я думаю, немного лучше показывает некоторые закономерности.
Наша первая задача - написать функцию repmin
на этом дереве. Мы также хотим написать его, используя производный экземпляр Traversable
. К счастью, паттерн, созданный repmin
, может быть выражен с помощью комбинации аппликативов для чтения и записи:
unloop :: WriterT a ((->) a) b -> b
unloop m =
let (x,w) = runWriterT m w
in x
repmin :: Ord a => Tree a -> Tree a
repmin = unloop . traverse (WriterT . f)
where
f x ~(Just (Min y)) = (y, Just (Min x))
Хотя мы используем версию WriterT
с преобразователем монад, конечно, мы не делаем ' Это необходимо, поскольку аппликативы всегда составляют.
Следующий шаг - превратить это в функцию repminPrint
: для этого нам понадобится расширение RecursiveDo
, которое позволяет нам t ie узел в функции unloop
, даже когда мы находимся внутри монады ввода-вывода.
unloopPrint :: WriterT a (ReaderT a IO) b -> IO b
unloopPrint m = mdo
(x,w) <- runReaderT (runWriterT m) w
pure x
repminPrint :: (Ord a, Show a) => Tree a -> IO (Tree a)
repminPrint = unloopPrint . traverse (WriterT . ReaderT . f)
where
f x ~(Just (Min y)) = (y, Just (Min x)) <$ print x
Правильно: на этом этапе нам удалось написать версию repminPrint
, которая использует любые общие c обход для выполнения функции repmin
. Конечно, он все еще в порядке, а не в ширину:
>>> repminPrint (1 :& [2 :& [4 :& []], 3 :& [5 :& []]])
1
2
4
3
5
Сейчас не хватает обхода, который проходит по дереву в порядке в ширину, а не в глубину. Я собираюсь использовать функцию, которую я написал здесь :
bft :: Applicative f => (a -> f b) -> Tree a -> f (Tree b)
bft f (x :& xs) = liftA2 (:&) (f x) (bftF f xs)
bftF :: Applicative f => (a -> f b) -> [Tree a] -> f [Tree b]
bftF t = fmap head . foldr (<*>) (pure []) . foldr f [pure ([]:)]
where
f (x :& xs) (q : qs) = liftA2 c (t x) q : foldr f (p qs) xs
p [] = [pure ([]:)]
p (x:xs) = fmap (([]:).) x : xs
c x k (xs : ks) = ((x :& xs) : y) : ys
where (y : ys) = k ks
В целом, что делает следующее однопроходным, repminPrint
в ширину * с использованием аппликативного обхода :
unloopPrint :: WriterT a (ReaderT a IO) b -> IO b
unloopPrint m = mdo
(x,w) <- runReaderT (runWriterT m) w
pure x
repminPrint :: (Ord a, Show a) => Tree a -> IO (Tree a)
repminPrint = unloopPrint . bft (WriterT . ReaderT . f)
where
f x ~(Just (Min y)) = (y, Just (Min x)) <$ print x
>>> repminPrint (1 :& [2 :& [4 :& []], 3 :& [5 :& []]])
1
2
3
4
5