将树的叶值更改为有序序列,同时保留树的结构

Change leaf values of a tree to ordered sequence while preserving the tree's structure

给定的是递归树结构

data Tree = Leaf Int | Node Tree Tree deriving Show

我想以一种保留树结构的方式对其进行归一化,但使叶子上的整数按深度优先顺序顺序排列。我怎样才能做到这一点?我当前的设置代码如下所示:

myTree = Node (Leaf 3) (Node (Leaf 5) (Leaf 2))

myTree' = normalize myTree

-- preserve tree structure, but make Ints sequential in depths-first traversal
normalize :: Tree -> Tree
normalize = id -- todo: implement

main = do
    print myTree  -- prints      : Node (Leaf 3) (Node (Leaf 5) (Leaf 2))
    print myTree' -- should print: Node (Leaf 1) (Node (Leaf 2) (Leaf 3))

不使用monads,你可以写一个函数映射树携带一定的状态

mapLRS :: (s -> Int -> (s, Int)) -> s -> Tree -> (Tree, s)
mapLRS f s (Leaf x  ) =
    let (s', x') = f s x
    in  (Leaf x', s')                  -- the mapped leaf and the new state
mapLRS f s (Node l r) =
    let (l', s' ) = mapLRS f s  l      -- the mapped left branch and the new state
        (r', s'') = mapLRS f s' r      -- the mapped right branch and the new state
    in  (Node l' r', s'')

现在

normalize :: Tree -> Tree
normalize = fst . mapWithStateLeftRight (\s _ -> (s + 1, s)) 1

使用 monad 可能更具可读性(f 与使用 state 的简单性不同)

import Control.Monad.State

mapLRS :: (s -> (Int, s)) -> Tree -> State s Tree
mapLRS f (Leaf   x) = Leaf <$> state f
mapLRS f (Node l r) = Node <$> mapLRS f l <*> mapLRS f r

normalize :: Tree -> Tree
normalize tree = evalState (mapLRS (\s -> (s, s + 1)) tree) 1

执行此操作的标准方法是在状态 monad 中记录当前标签:

import Control.Monad.State

data Tree = Leaf Int | Node Tree Tree deriving (Show)

normalize :: Tree -> Tree
normalize t = evalState (go t) 1 where

  go :: Tree -> State Int Tree
  go (Leaf _)   = Leaf <$> (get <* modify (+1))
  go (Node l r) = Node <$> go l <*> go r

此解决方案将状态初始化为 1,然后遍历树并在每次遇到 Leaf 时将当前标签放入返回的 Leaf 并递增标签。

或者,我们可以为 Tree 导出 Traversable 实例:

{-# language DeriveFunctor, DeriveFoldable, DeriveTraversable #-}

import Control.Monad.State

data Tree a = Leaf a | Node (Tree a) (Tree a)
  deriving (Show, Functor, Foldable, Traversable)

normalize :: Tree a -> Tree Int
normalize = (`evalState` 1) . traverse (\_ -> get <* modify (+1))

这以相同的方式工作,但请注意,它依赖于派生的 Traversable 实例具有正确的遍历顺序这一事实。当然,如果我们想要一些其他顺序,那么我们将不得不编写自己的遍历。