对于编程练习,我应该采用数据树类型
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) }
以下是label
和run
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
根本不受影响。非常感谢任何帮助。
答案 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
将执行有序遍历。