我需要通过深度优先顺序遍历来标记二叉树,因此我认为我需要首先遍历树的左侧分支并标记它们,然后对正确的分支执行相同操作。我的二叉树只在内部节点(而不是在节点/叶子端)存储值:
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))
答案 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,您的程序将执行完全相同的操作 (“下一个标签”是国家),但符号不同, 这可能有益也可能没有益处(对于学习/理解)。