Haskell通过深度优先顺序遍历标记二叉树

时间:2014-10-16 09:49:10

标签: haskell tree binary-search-tree

我需要通过深度优先顺序遍历来标记二叉树,因此我认为我需要首先遍历树的左侧分支并标记它们,然后对正确的分支执行相同操作。我的二叉树只在内部节点(而不是在节点/叶子端)存储值:

label :: MonadState m Int => Tree a -> m (Tree (Int, a))
label (Branch l x r) = do n <- get
                          l' <- label l
                          r' <- label r
                          return (Branch l' (n, x) r')
label (Leaf) = return Leaf

*编辑:需要使用State Monad但是我并没有真正掌握它的用法。我当前的代码如上所示,但无法正常工作。

编辑:期望的结果,例如为:

Branch (Branch Leaf (-2) Leaf) 1 Leaf

应该是:

Branch (Branch Leaf (0,-2) Leaf) (1,1) Leaf

此外,我不确定我应该如何使用State Monad,我仍然对它的使用感到困惑:

instance Monad (State' s) where

    -- return :: a -> State' s a
    return x = State' (\(s,c) -> (x, s, (c <> oneReturn) ))

    -- (>>=) :: State' s a -> (a -> State' s b) -> State' s b
    st >>= k = State' $ \(s,c) -> let (a, s', c') = runState' st (s,c)
                                  in runState' (k a) (s',(c' <> oneBind) )

instance MonadState (State' s) s where

    -- get :: State' s s
    get = State' $ \(s,c) -> (s,s, (c <> oneGet))

    -- put :: s -> State' s ()
    put s = State' $ \(_,c) -> ((),s, (c <> onePut))

3 个答案:

答案 0 :(得分:16)

我担心您的问题比目前为止提出的答案更容易解决,只要您了解如何利用您在制作类型定义时确定的结构。这是怎么回事。

{-# LANGUAGE DeriveTraversable, FlexibleContexts #-}

module LBT where

import Control.Monad.State

data Tree x
  = Leaf
  | Branch (Tree x) x (Tree x)
  deriving (Functor, Foldable, Traversable)

这使得traverse操作从左到右工作,即根据需要按顺序工作。

现在解释如何处理单个元素。

next :: MonadState Int m => m Int
next = do x <- get ; modify (+ 1) ; return x

labelElt :: MonadState Int m => x -> m (Int, x)
labelElt x = (,) <$> next <*> pure x

next操作为您提供下一个值并更新计数器。然后labelElt操作用其计数器修饰单个值。现在

label :: MonadState Int m => Tree x -> m (Tree (Int, x))
label = traverse labelElt

当您定义类型时,您将获得已支付的程序。当您知道如何处理一个元素时,您可以管理整个结构。免费。这里不需要临时递归!您的类型结构提供了程序的结构。只要你愿意,Haskell就会为你做到这一点。

答案 1 :(得分:1)

使用State monad构建函数的一种方法是:

label :: MonadState m Int => Tree a -> m (Tree (Int, a))
label t = do
    -- Fetch the next label number from the state monad with "get".
    -- Increase the label number by one and store it back in the state with "put".
    -- Pattern match on "t" and call "label" recursively.

我假设您已经熟悉do - 语法?尝试编写上述函数,如果需要更多提示,请使用新代码更新问题。

答案 2 :(得分:1)

您不需要label_l,label_r函数。你不需要monad。 (您可以使用此示例来了解状态monad,但您不必这样做。)

只需使用(标准技巧)来丰富

的规范
Tree a -> Tree (Int, a)

f :: Int -> Tree a -> ( Tree (Int a), Int )

函数获取开始标签(第一个参数)并返回标记树和下一个要使用的标签(在结果的第二个组件中)。

,例如,

f 8 (Branch (Branch Leaf (-2) Leaf) 1 Leaf)
   == ( Branch (Branch Leaf (8,-2) Leaf) (9,1) Leaf), 10 )

这可以很容易地实现,没有任何高级概念。

f s0 t = case t of
     Branch l k r -> let (l', s1) = f s0 l
                         (r', s2) = f s1 r
                     in  ...

使用状态monad,您的程序将执行完全相同的操作 (“下一个标签”是国家),但符号不同, 这可能有益也可能没有益处(对于学习/理解)。