这个例子来源于scala圣经级教程《Functional Programming in Scala》,由于本人跟着书中的代码敲了一遍,然后写了点测试代码验证了一下正确性,所以就放在这做个备忘吧。贴出来只是为了方便自己,如果看不懂,但是又感兴趣的就去看原书吧……
package state
import RNG.Simple
import state.State._
case class State[S, +A](run: S => (A, S)) {
def map[B](f: A => B): State[S, B] = flatMap(a => unit(f(a)))
def flatMap[B](f: A => State[S, B]): State[S, B] = State(s => {
val (a, s1) = run(s)
f(a).run(s1)
})
def map2[B, C](sb: State[S, B])(f: (A, B) => C): State[S, C] = flatMap(a => sb.map(b => f(a, b)))
}
object State {
type Rand[A] = State[RNG, A]
def unit[S, A](a: A): State[S, A] = State(s => (a, s))
def get[S]: State[S, S] = State(s => (s, s))
def set[S](s: S): State[S, Unit] = State(_ => ((), s))
def modify[S](f: S => S): State[S, Unit] = for {
s <- get
_ <- set(f(s))
} yield ()
// The idiomatic solution is expressed via foldRight
def sequenceViaFoldRight[S, A](sas: List[State[S, A]]): State[S, List[A]] =
sas.foldRight(unit[S, List[A]](List()))((f, acc) => f.map2(acc)(_ :: _))
def sequence[S, A](sas: List[State[S, A]]): State[S, List[A]] = {
def go(s: S, actions: List[State[S,A]], acc: List[A]): (List[A], S) =
actions match {
case Nil => (acc.reverse, s)
case h :: t => h.run(s) match { case (a,s2) => go(s2, t, a :: acc) }
}
State((s: S) => go(s, sas, List()))
}
def sequenceViaFoldLeft[S,A](l: List[State[S, A]]): State[S, List[A]] =
l.reverse.foldLeft(unit[S, List[A]](List()))((acc, f) => f.map2(acc)( _ :: _ ))
def main(args: Array[String]): Unit = {
val simple = Simple(System.currentTimeMillis())
println(simple)
println(State.unit[RNG, Int](5).run(simple))
val li5Ele = List.fill(5)(State.unit[RNG, Int](5))
val sequenceFun = State.sequence(li5Ele)
val sequenceVal = State.sequence(li5Ele).run(simple)
println(sequenceVal)
val li6Ele = List.fill(6)(State(RNG.int))
val sequence6EleVal = State.sequence(li6Ele).run(simple)
println(sequence6EleVal)
val sequenceViaFoldRightVal = State.sequenceViaFoldRight(li6Ele).run(simple)
println(sequenceViaFoldRightVal)
val sequenceViaFoldLeftVal = State.sequenceViaFoldLeft(li6Ele).run(simple)
println(sequenceViaFoldLeftVal)
println(State.set(simple).run(simple)._1)
println(State.set(simple).run(simple)._2)
println("---------------------------------------------------------------")
println(State.get.run(simple)._1)
println(State.get.run(simple)._2)
println("---------------------------------------------------------------")
println(State.get[RNG].map[Int](_.nextInt._1).run(simple))
println(State.get[RNG].map[RNG](_.nextInt._2).run(simple))
//对传入的simple对象调用一次nextInt方法,从而转换为另一个simple对象
println(State.set(simple.nextInt._2).run(simple))
//对传入的simple对象调用一次nextInt方法,从而转换为另一个simple对象
val modifyVal =State.modify((a: RNG) => a.nextInt._2).run(simple)
println(modifyVal)
///
/**
* 模拟交通灯颜色的变化
* @param a
* @return
*/
def trafficLight(a: String): (String, String) = a match {
case "红" => ("红", "绿")
case "绿" => ("绿", "黄")
case "黄" => ("黄", "红")
case _ => ("红", "绿")
}
val li5 = List.fill(10)(State((a: String) => trafficLight(a)))
val redStart = State.sequence[String, String](li5).run("红")
println(redStart)
val greenStart = State.sequence[String, String](li5).run("绿")
println(greenStart)
val xStart = State.sequence[String, String](li5).run("xxx")
println(xStart)
///
val machine = Machine(true, 4, 0)
val inputs = List(Coin, Turn, Coin, Turn, Coin, Turn, Coin, Turn, Coin, Turn, Coin, Turn)
val machineRes = Candy.simulateMachine(inputs).run(machine)
println(machineRes)
val _machineRes = Candy._simulateMachine((inputs)).run(machine)
println(_machineRes)
}
}
sealed trait Input
/**
* 表示一枚硬币
*/
case object Coin extends Input
/**
* 表示糖果售货机的按钮
*/
case object Turn extends Input
/**
* 表示糖果售货机:机器有2种输入方式,可以投入硬币,也可以按动按钮获取糖果
* 机器有2种状态:锁定状态和非锁定状态
*/
case class Machine(locked: Boolean, candies: Int, coins: Int)
object Candy {
/**
* 对锁定的售货机投入硬币,如果有剩余的糖果它将变为非锁定状态
* 对非锁定状态的售货机按下按钮,它将给出糖果并变回锁定状态
* 对锁定状态的售货机按下按钮,或对非锁定状态的售货机投入硬币,什么也不做
*/
def update = (i: Input) => (s: Machine) =>
(i, s) match {
case (_, Machine(_, 0, _)) => s
case (Coin, Machine(false, _, _)) => s
case (Turn, Machine(true, _, _)) => s
case (Coin, Machine(true, candy, coin)) => Machine(false, candy, coin + 1)
case (Turn, Machine(false, candy, coin)) => Machine(true, candy - 1, coin)
}
/**
* 对一个糖果售货机建模的有限状态机
*/
def simulateMachine(inputs: List[Input]): State[Machine, (Int, Int)] = for {
_ <- sequence(inputs map (modify[Machine] _ compose update))
s <- get
} yield (s.coins, s.candies)
/**
* _simulateMachine is equal to simulateMachine, but not so brief
* _simulateMachine contains debug statements
*/
def _simulateMachine(inputs: List[Input]): State[Machine, (Int, Int)] = {
val machines = for {
ele <- inputs
} yield modify[Machine]({ print(ele); print(" "); update(ele)})
// print(ele); print(" ") for debug purpose
for {
_ <- sequence(machines)
s <- get
} yield (s.coins, s.candies)
}
}
上述代码的运行结果是:
Simple(1530871197922)
(5,Simple(1530871197922))
(List(5, 5, 5, 5, 5),Simple(1530871197922))
(List(760619510, 1890169447, -624214092, 759529940, -1356984262, -171369807),Simple(270244085079856))
(List(760619510, 1890169447, -624214092, 759529940, -1356984262, -171369807),Simple(270244085079856))
(List(760619510, 1890169447, -624214092, 759529940, -1356984262, -171369807),Simple(270244085079856))
()
Simple(1530871197922)
---------------------------------------------------------------
Simple(1530871197922)
Simple(1530871197922)
---------------------------------------------------------------
(760619510,Simple(1530871197922))
(Simple(49847960230981),Simple(1530871197922))
((),Simple(49847960230981))
((),Simple(49847960230981))
(List(红, 绿, 黄, 红, 绿, 黄, 红, 绿, 黄, 红),绿)
(List(绿, 黄, 红, 绿, 黄, 红, 绿, 黄, 红, 绿),黄)
(List(红, 绿, 黄, 红, 绿, 黄, 红, 绿, 黄, 红),绿)
((4,0),Machine(true,0,4))
Coin Turn Coin Turn Coin Turn Coin Turn Coin Turn Coin Turn ((4,0),Machine(true,0,4))
注:
代码中的RNG参见本人同类文章《用Scala实现一个纯函数风格的引用透明的伪随机数生成器》