Haskell语言学习笔记(9)State Monad

State Monad

旧的定义
newtype State s a = State { runState :: s -> (a,s) }
instance Monad (State s) where
    return x = State $ \s -> (x,s)
    (State h) >>= f = State $ \s -> let (a, newState) = h s
                                        (State g) = f a
                                    in  g newState
这里旧的定义是指API更改之前的定义,也就是Learn You a Haskell for Great Good!(中文版Haskell趣学指南)这本书中的定义。
  • newtype State s a = State { runState :: s -> (a,s) }
    State 类型是个 newtype,也就是对现有类型的封装。该类型有两个类型参数:表示状态的类型参数 s 以及表示结果的类型参数 a。
    State 类型封装的是一个状态转换函数:\s -> (a,s'),通过 runState 字段可以从 State 类型中取出这个函数。
    该函数接收一个状态参数 s,经过计算(转换)之后返回一对值:计算结果 a 以及新的状态 s‘。
  • instance Monad (State s) where
    State s 类型是 Monad 类型类的一个实例。
    对比 Monad 类型类的定义,可知 return 函数的类型签名为:
    return :: x -> State s x
    大致相当于 x -> s -> (x,s)
    而 bind 函数的类型签名为:
    (>>=) :: State s a -> (a -> State s b) -> State s b
    大致相当于 (s -> (a,s)) -> (a -> s -> (b,s)) -> (s -> (b,s))
  • return x = State $ \s -> (x,s)
    return 函数将 x 封装进了状态转换函数,该函数把结果值设为 x,状态值 s 则保持不变。
  • (State h) >>= f = State $ \s ->
    对比函数签名,可知 h 的类型为 s -> (a,s)。
    而 f 的类型为 a -> State s b,大致相当于 a -> s -> (b,s)
    bind 函数组合两个状态转换函数,最终结果仍然是个状态转换函数。
  • let (a, newState) = h s
    这里将状态转换函数 h 应用于状态 s 上,得到结果值 a 以及新的状态值 newState。
  • (State g) = f a
    根据 f 的类型 a -> State s b(大致相当于 a -> s -> (b,s)),可知 g 的类型为 s -> (b,s),这里 g 是第二个状态转换函数。
  • in g newState
    这里将状态转换函数 g 应用于状态 newState 上,从而得出最终的结果值以及更新的状态值。
新的定义
newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
instance (Monad m) => Monad (StateT s m) where
    return a = StateT $ \s -> return (a, s)
    m >>= k  = StateT $ \s -> do
        ~(a, s') <- runStateT m s
        runStateT (k a) s'

instance (Functor m) => Functor (StateT s m) where
    fmap f m = StateT $ \s ->
        fmap (\ ~(a, s') -> (f a, s')) $ runStateT m s

type State s = StateT s Identity
根据新的定义,State 类型只是 StateT 类型的一个特例。

State Monad 函数

  • state f:将函数 f 封装进State Monad。
  • runState m X:将函数 f 从State Monad中提取出来,调用 f X,同时返回结果值 a 和状态值 s。
  • evalState m X:将函数 f 从State Monad中提取出来,调用 f X,但是仅返回结果值 a。
  • execState m X:将函数 f 从State Monad中提取出来,调用 f X,但是仅返回状态值 s。
  • return X:结果值 a 设为 X,状态值 s 不变。
  • get:结果值 a 设为状态值 s,状态值 s 不变。
  • put X:状态值 s 设为 X,结果值 a 设为空。
  • modify f:状态值 s 设为 f s,结果值 a 设为空。
  • gets f:结果值 a 设为 f s,状态值 s 不变。
Prelude Control.Monad.State> runState (return 15) 1
(15,1)
Prelude Control.Monad.State> runState get 1
(1,1)
Prelude Control.Monad.State> runState (put 3) 1
((),3)
Prelude Control.Monad.State> runState (modify (+1)) 1
((),2)
Prelude Control.Monad.State> runState (gets (+1)) 1
(2,1)
Prelude Control.Monad.State> evalState (gets (+1)) 1
2
Prelude Control.Monad.State> execState (gets (+1)) 1
1
Prelude Control.Monad.State> runState (do put 3; return 15) 1
(15,3)
Prelude Control.Monad.State> runState (put 3 >> return 15) 1
(15,3)

随机数

Prelude Control.Monad.State System.Random> let r = state $ randomR (1, 10)
Prelude Control.Monad.State System.Random> do {gen <- newStdGen; return $ evalState r gen}
4
Prelude Control.Monad.State System.Random> newStdGen >>= \gen -> return $ evalState r gen
3

阶乘

fact_state :: State Int Int
fact_state = get >>= \x -> if x <= 1
                           then return 1
                           else (put (x - 1) >> fmap (*x) fact_state)

factorial :: Int -> Int
factorial = evalState fact_state

Prelude Control.Monad.State.Strict Control.Applicative> factorial <$> [1..10]
[1,2,6,24,120,720,5040,40320,362880,3628800]

斐波那契数列

fibs_state :: State (Int, Int, Int) Int
fibs_state = get >>= \(x1, x2, n) -> if n == 0
                                     then return x1
                                     else (put (x2, x1+x2, n-1) >> fibs_state)

fibonacci n = evalState fibs_state (0, 1, n)

Prelude Control.Monad.State.Strict Control.Applicative> fibonacci <$> [1..10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

参考链接

Confusion over the State Monad code on “Learn you a Haskell”
The State Monad: A Tutorial for the Confused?
Three Useful Monads
Where is the data constructor for 'State'?
Implementing factorial and fibonacci using State monad (as a learning exercise)

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值