设为首页 加入收藏

TOP

Scalaz(17)- Monad:泛函状态类型-State Monad(二)
2017-10-10 12:13:29 】 浏览:8458
Tags:Scalaz Monad 状态 类型 State
ndexedStateT(s => F.bind(apply(s)) { case (s1, a) => f(a)(s1) })

特别注意flatMap:F必须是Monad,这样就可以在连接两个IndexedStateT时先后运行它们的状态运算函数S1=>F[(S2,A)],即:apply(s)和f(a)(s1)。

如果不出意料的话,IndexedStateT的构建方式就是传入一个状态运算函数S1=>F[(S2,A)]:

object IndexedStateT extends StateTInstances with StateTFunctions { def apply[F[_], S1, S2, A](f: S1 => F[(S2, A)]): IndexedStateT[F, S1, S2, A] = new IndexedStateT[F, S1, S2, A] { def apply(s: S1) = f(s) } }

传入的函数f实现了抽象函数run使IndexedStateT实例化。

State Monad应该需要一套读写、传递状态的方法。这些方法可以在MonadState trait里找到:scalaz/MonadState.scala

trait MonadState[F[_,_],S] extends Monad[({type f[x]=F[S,x]})#f] { def state[A](a: A): F[S, A] = bind(init)(s => point(a)) def constantState[A](a: A, s: => S): F[S, A] = bind(put(s))(_ => point(a)) def init: F[S, S] def get: F[S, S] def gets[A](f: S => A): F[S, A] = bind(init)(s => point(f(s))) def put(s: S): F[S, Unit] def modify(f: S => S): F[S, Unit] = bind(init)(s => put(f(s))) } object MonadState { def apply[F[_,_],S](implicit F: MonadState[F, S]) = F }

MonadState是个抽象类型,因为它继承了Monad类但并没有实现Monad的抽象函数point和bind。所以这些状态维护函数必须在MonadState子类实例存在的情况下才能使用。这个情况在object MonadState里的apply函数的隐式参数F可以推断得出。IndexedStateT就是MonadState的子类,所以通过IndexedStateT的实例来施用状态运算函数是没用什么问题的。以下是这些操作函数的实现:

private trait StateTMonadState[S, F[_]] extends MonadState[({type f[s, a] = StateT[F, s, a]})#f, S] { implicit def F: Monad[F] def bind[A, B](fa: StateT[F, S, A])(f: A => StateT[F, S, B]): StateT[F, S, B] = fa.flatMap(f) def point[A](a: => A): StateT[F, S, A] = { lazy val aa = a StateT(s => F.point(s, aa)) } def init: StateT[F, S, S] = StateT(s => F.point((s, s))) def get = init def put(s: S): StateT[F, S, Unit] = StateT(_ => F.point((s, ()))) override def modify(f: S => S): StateT[F, S, Unit] = StateT(s => F.point((f(s), ()))) override def gets[A](f: S => A): StateT[F, S, A] = StateT(s => F.point((s, f(s)))) }

我们现在可以尝试一些简单的State Monad使用案例,先试着模仿一个数字堆栈(Integer Stack)操作:

1   type Stack = List[Int] 2   def pop: State[Stack, Int] = State { case h::t => (t,h) } 3                                                   //> pop: => scalaz.State[Exercises.stateT.Stack,Int]
4   def push(a: Int): State[Stack, Unit] = State { xs => (a :: xs, ()) } 5                                                   //> push: (a: Int)scalaz.State[Exercises.stateT.Stack,Unit]

pop和push操作结果都是State,State是Monad,这样我们就可以用for-comprehension来演示具体操作了:

 1  val prg = for {  2     _ <- push(1)  3     _ <- push(2)  4     _ <- push(3)  5     a <- pop  6     b <- get
 7     _ <- pop  8     _ <- put(List(9))  9   } yield b                                       //> prg : scalaz.IndexedStateT[scalaz.Id.Id,Exercises.stateT.Stack,List[Int],E 10                                                   //| xercises.stateT.Stack] = scalaz.IndexedStateT$$anon$10@72d1ad2e
11   prg.run(List())                                 //> res2: scalaz.Id.Id[(List[Int], Exercises.stateT.Stack)] = (List(9),List(2, 12                                                   //| 1))

prg只是一段功能描述,因为状态运算函数是个lambda: s => (s,a)。这里s是个未知数,它在for loop里逐层传递下去。运算结果需要通过运行run函数并提供初始状态值List()后才能获取,也就是说真正的运算是在运行run时才开始的。我们称run为程序prg的翻译器(interpreter),这是函数式编程的典型模式,这样可以把具体运算延到最后。

我们再看看如何读写状态:

 1   val prg = for {  2     _ <- push(1)  3     _ <- push(2)  4     _ <- push(3)  5     a <- pop  6     b <- get     //(s,s)
 7     c <- gets { s:Stack => s.length} //(s,s.length)
 8     _ <- pop  9     _ <- put(List(9))  //(List(9),a)
10     _ <- modify {s:Stack => s ++ List(10) } //(List(9,10),a)
11   } yield c                                       //> prg : scalaz.IndexedStateT[scalaz.Id.Id,Exercises.stateT.Stack,List[Int],I 12                                                   //| nt] = scalaz.IndexedStateT$$anon$10@72d1ad2e
13   prg.run(List()
首页 上一页 1 2 3 4 5 下一页 尾页 2/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Scalaz(16)- Monad:依赖注入.. 下一篇Scalaz(18)- Monad: ReaderWr..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目