繁体   English   中英

树插入使用 State Monad

[英]Tree Insert using State Monad

我有一个树和插入操作,如“Learn You a Haskell for Great Good:”中定义的:

data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq) 

treeInsert :: (Ord a) => a -> Tree a -> Tree a  
treeInsert x EmptyTree = Node x EmptyTree EmptyTree
treeInsert x (Node a left right)   
    | x == a = Node x left right  
    | x < a  = Node a (treeInsert x left) right  
    | x > a  = Node a left (treeInsert x right)   

我想使用 State Monad 重新实现treeInsert ,但我什至不确定 function 声明应该是什么样子。 到目前为止我有这个:

treeInsert :: (Ord a) => a -> Tree a -> State (Tree a) a

您将如何使用 State Monad 编写treeInsert

警告:此答案包含剧透。

您可以相当轻松地围绕现有的treeInsert function 编写一个包装器,允许您以您想要的方式使用 do-notation。 As per the comments, there's a function modify that takes a modifying function f:: s -> s and turns it into a State s () which is an "action" to modify a state s . 这意味着你可以写:

stateTreeInsert :: (Ord a) => a -> State (Tree a) ()
stateTreeInsert x = modify (treeInsert x)

或更简洁地说:

stateTreeInsert :: (Ord a) => a -> State (Tree a) ()
stateTreeInsert = modify . treeInsert

然后,您可以定义一个“动作”,例如:

insertSomeStuff :: (Ord a, Num a) => State (Tree a) ()
insertSomeStuff = do
  stateTreeInsert 0
  stateTreeInsert 1
  stateTreeInsert 2

然后使用execState将其应用于特定树:

main = print $ execState insertSomeStuff EmptyTree

但是,我猜您对以状态操作形式从头开始重新实现treeInsert更感兴趣。

问题是这样做的“直截了当”的方式不是很有趣或惯用的。 这很尴尬。 它看起来像这样:

awkwardTreeInsert :: (Ord a) => a -> State (Tree a) ()
awkwardTreeInsert x = do
  t <- get
  case t of
    EmptyTree -> put $ Node x EmptyTree EmptyTree
    Node a l r -> case compare x a of
      LT -> do put l                 -- replace tree with left child
               awkwardTreeInsert x   -- insert into left child
               l' <- get             -- get the answer
               put $ Node a l' r     -- overwrite with whole tree w/ updated left child
      GT -> do put r
               awkwardTreeInsert x
               r' <- get
               put $ Node a l r'
      EQ -> return ()

这里的问题是 state,正如我们所写的,一次只能容纳一棵树。 所以,如果我们想递归调用算法向分支中插入一些东西,我们需要用它的一个孩子覆盖“大树”,运行递归插入,得到答案,然后用“大树”覆盖它更换适当的孩子。

无论如何,它的工作方式与stateTreeInsert相同,因此:

insertSomeStuff :: (Ord a, Num a) => State (Tree a) ()
insertSomeStuff = do
  awkwardTreeInsert 0
  awkwardTreeInsert 1
  awkwardTreeInsert 2

main = print $ execState insertSomeStuff EmptyTree

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM