修改monad状态

时间:2016-10-23 10:00:18

标签: haskell tree state monads

对于编程练习,我应该采用数据树类型

data Tree a = Branch (Tree a) a (Tree a) | Leaf
    deriving (Eq, Ord, Show)

并使用状态monad,使用a标记每个Int,越来越深,先按顺序排列,并计算monadic动作的数量。例如,表达式

let tree = Branch (Branch Leaf "B" Leaf) "A" Leaf
in run (label tree) 42

应评估为

(Branch (Branch Leaf (42, "B") Leaf) (43, "A") Leaf
, Counts {binds = 10,returns = 5, gets = 4, puts = 2})

州的类型是:

newtype State' s a = State' { runState' :: (s, Counts) -> (a, s, Counts) }

以下是labelrun

的实施方式
label :: MonadState m Int => Tree a -> m (Tree (Int, a))
label Leaf                      = return Leaf
label (Branch left value right) = do
                                  newLeft  <- label left
                                  int <- get
                                  put (int + 1)
                                  newRight <- label right
                                  return (Branch newLeft (int, value) newRight)


run :: State' s a -> s -> (a, Counts)
run s ns = let (a, _, counts) = runState' s (ns, Counts 0 0 0 0) in (a, counts)

但是,当我运行测试用例时,我的结果是

(Branch (Branch Leaf (42,"B") Leaf) (42,"A") Leaf
, Counts {binds = 12, returns = 5, gets = 6, puts = 2})

似乎Int根本没有更新。这很奇怪,因为分配的每个部分都有单独的测试用例,除此之外的所有内容都是正确的。无论如何,这里是get和put实现:

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

-- put :: s -> State' s ()
put x = State' (\(x, counts) -> ((), x, counts <> onePut))

我真的很茫然。我不知道为什么Int根本不受影响。非常感谢任何帮助。

2 个答案:

答案 0 :(得分:1)

问题在于

put x = State' (\(x, counts) -> ((), x, counts <> onePut))

这里你应该将x置于一个状态,但它会以(x, counts)模式被遮蔽。做到这一点

put x = State' (\(_, counts) -> ((), x, counts <> onePut))
只要你不关心monad法则,你应该没事,因为你的任务迫使你违反它们:

  

计算monadic动作的数量

其中一项法律是(return x >>= f) ~ f x,但前一种表达法中还有其他return(>>=)

答案 1 :(得分:1)

我知道这是一项任务,但我想指出GHC可以为你编写几乎所有这些代码!神奇的单词是deriving Traversable

{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
data Tree a = Leaf
            | Node (Tree a) a (Tree a)
            deriving (Functor, Foldable, Traversable)

Traversable类抽象出对结构的每个元素执行操作的概念。 traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)采用对元素Applicative执行a效果的函数,并在整个结构t上运行,对效果进行排序以生成t Applicative上下文。

所以我们要做的就是说如何对单个元素采取行动,

inc :: a -> State Int (Int, a)
inc x = do
    counter <- get
    put (counter + 1)
    return (counter, x)

并且Traversable机器将在整个树上运行动作。

label :: Tree a -> Tree (Int, a)
label t = evalState (traverse inc t) 0

Node构造函数的布局决定了遍历顺序;在这种情况下,traverse将执行有序遍历。