对于OOP程序员来说,泛函状态变迁(functional state transition)是一个陌生的课题。泛函状态变迁是通过泛函状态数据结构(functional state)来实现的。State是一个出现在泛函编程里的类型(type)。与其它数据类型一样,State同样需要自身的一套泛函操作函数和组合函数(combinators),我们将在以下章节中讨论有关State数据类型的设计方案。
在正式介绍State类型前,我们先从随意数产生器(RNG: Random Number Generator)开始,从泛函RNG的原理分析引领到State设计方案。
先看看我们熟悉的java RNG:
//java code val rng = new java.util.Random //> rng : java.util.Random = java.util.Random@48533e64 rng.nextInt //> res0: Int = -1412360869 rng.nextInt //> res1: Int = 645622520 rng.nextDouble //> res2: Double = 0.4216477872043267 rng.nextDouble //> res3: Double = 2.306856098814869E-4
trait RNG { def nextInt: (Int, RNG) }
假设我们有这么个结果:
class Foo { var s: FooState = ... def bar: Bar def baz: Int }
trait Foo { def bar: (Bar, Foo) def baz: (Int, Foo) }
trait RNG { def nextInt: (Int, RNG) } //起始状态RNG, 种子RNG case class seedRNG(seed: Long) extends RNG { def nextInt: (Int, RNG) = { val seed2 = (seed*0x5DEECE66DL + 0xBL) & ((1L << 48) - 1) ((seed2 >>> 16).asInstanceOf[Int], seedRNG(seed2)) } } val rng = seedRNG(System.currentTimeMillis) //> rng : ch6.rng.seedRNG = seedRNG(1427201377395) val (i,rng2) = rng.nextInt //> i : Int = 1516062234 //| rng2 : ch6.rng.RNG = seedRNG(99356654630658) val (i2,rng3) = rng2.nextInt //> i2 : Int = 483165502 //| rng3 : ch6.rng.RNG = seedRNG(31664734402533) val (i3,rng4) = rng2.nextInt //> i3 : Int = 483165502 //| rng4 : ch6.rng.RNG = seedRNG(31664734402533)
所有类型的泛函式随意数产生器都可以从Int RNG nextInt推导出来:
object RNG { //值在 0.0 - 1.0 之间的Double随意数 def nextDouble(rng: RNG): (Double, RNG) = { val (i,rng2) = rng.nextInt if ( i == Int.MaxValue ) (0.0, rng2) else ( i.toDouble / Int.MaxValue.toDouble, rng2) } def nextPositiveInt(rng: RNG): (Int, RNG) = { val (i, rng2) = rng.nextInt if ( i == Int.MaxValue ) (Int.MaxValue, rng2) else (i.abs, rng2) } def nextBoolean(rng: RNG): (Boolean, RNG) = { rng.nextInt match { case (i, rng2) => (i % 2 == 0, rng2) } } //产生一个随意tuple (Int, Double) def nextIntDouble(rng: RNG): ((Int, Double), RNG) = { val (i,rng2) = nextPositiveInt(rng) val (d,rng3) = nextDouble(rng2) ((i,d),rng3) } //产生一个随意数的n长度List def nextInts(n: Int)(rng: RNG): (List[Int], RNG) = { def go(n: Int, rng: RNG, acc: List[Int]): (List[Int], RNG) = { if ( n <= 0 ) (acc, rng) else { val (i,rng2) = rng.nextInt go(n-1,rng2,i :: acc) } } go(n,rng,Nil: List[Int]) } } import RNG._ val rng = seedRNG(System.currentTimeMillis) //> rng : ch6.rng.seedRNG = seedRNG(1427204725766) val (d, rng5) = nextDouble(rng) //> d : Double = 0.6090536781628866 //| rng5 : ch6.rng.RNG = seedRNG(85716684903065) val (b, rng6) = nextBoolean(rng5) //> b : Boolean = false //| rng6 : ch6.rng.RNG = seedRNG(123054239736112) val ((i5,d2), rng7) = nextIntDouble(rng6) //> i5 : Int = 1054924659 //| d2 : Double = 0.8877875771782303 //| rng7 : ch6.rng.RNG = seedRNG(124944993788778) val (ints, rng8) = nextInts(5)(rng7) //> ints : List[Int] = List(-782449771, -1992066525, -825651621, -440562357, 7 //| 00809062) //| rng8 : ch6.rng.RNG = seedRNG(230196348539649)
从以上的例子中可以发现这些函数一致的款式:func(RNG):(A,RNG),即:RNG => (A,RNG), 是lambda function,纯纯的函数类型申明。这样看来随意数产生器就是一个函数类型,我们可以把产生器当作函数的参数或者返回值来使用。那么我们试着自创一个新的类型:
type Rand[+A] = RNG => (A, RNG)
type Rand[+A] = RNG => (A, RNG) def rnInt: Rand[Int] = _.nextInt def rnPositiveInt: Rand[Int] = nextPositiveInt } import RNG._ val rng = seedRNG(System.currentTimeMillis) //> rng : ch6.rng.seedRNG = seedRNG(1427239224137) rnInt(rng) //> res0: (Int, ch6.rng.RNG) = (-1225681606,seedRNG(201148706995232)) rnPositiveInt(rng) //> res1: (Int, ch6.rng.RNG) = (1225681606,seedRNG(201148706995232))
既然我们已经把随意数产生器变成了Rand类型,我们应该可以方便地对随意数产生器进行组合、变形了吧?先看一个最基本的组件(combinator):
def unit[A](a: A): Rand[A] = { rng => (a, rng) }
再来一个针对Rand类型的map:
def map[A,B](ra: Rand[A])(f: A => B): Rand[B] = { rng => { val (x, rng2) = ra(rng) (f(x), rng2) } }
举出个使用map的例子:
def rnDouble: Rand[Double] = { map(rnPositiveInt){ _ / (Int.MaxValue.toDouble + 1) } }
val rng = seedRNG(System.currentTimeMillis) //> rng : ch6.rng.seedRNG = seedRNG(1427245671833) rnDouble(rng) //> res0: (Double, ch6.rng.RNG) = (0.6156660546548665,seedRNG(86647294261296))
def map2[A,B,C](ra: Rand[A], rb: Rand[B])(f: (A,B) => C): Rand[C] = { rng => { val (x,rng2) = ra(rng) val (y,rng3) = rb(rng2) (f(x,y), rng3) } }
def rnPair[A,B](ra: Rand[A], rb: Rand[B]): Rand[(A, B)] = { map2(ra,rb){(_,_)} } def rnIntDoublePair: Rand[(Int, Double)] = { rnPair(rnInt,rnDouble) } def rnDoubleIntPair: Rand[(Double, Int)] = { rnPair(rnDouble,rnInt) } } import RNG._ val rng = seedRNG(System.currentTimeMillis) //> rng : ch6.rng.seedRNG = seedRNG(1427246457588) rnIntDoublePair(rng) //> res0: ((Int, Double), ch6.rng.RNG) = ((-1302173232,0.355998701415956),seedR //| NG(231372613633230)) rnDoubleIntPair(rng) //> res1: ((Double, Int), ch6.rng.RNG) = ((0.6063716635107994,-764501390),seedR //| NG(231372613633230))
//用递归方式 def sequence[A](lr: List[Rand[A]]): Rand[List[A]] = { rng => { def go(xs: List[Rand[A]], r: RNG, acc: List[A]): (List[A], RNG) = { lr match { case Nil => (acc,rng) case h :: t => { val (x, rng2) = h(rng) go(t,rng2,x::acc) } } } go(lr,rng,List()) } } //用foldRight实现 def sequence_1[A](lr: List[Rand[A]]): Rand[List[A]] = { lr.foldRight(unit(Nil: List[A])) {(h,t) => map2(h,t)(_ :: _)} }
def positiveInt: Rand[Int] = { map(int) { i => if (i != Int.MinValue) i.abs else ?? } }
赶快实现flatMap:
def flatMap[A,B](ra: Rand[A])(f: A => Rand[B]): Rand[B] = { rng => { val (x, rng2) = ra(rng) f(x)(rng2) } } def positiveIntByFlatMap: Rand[Int] = { flatMap(rnInt) { a => { if ( a != Int.MinValue) unit(a.abs) else positiveIntByFlatMap } } }
没错,可以用flatMap实现positiveInt了。那么用flatMap可以实现map,map2吗?看看下面的具体实现方法:
def mapByFlatMap[A,B](ra: Rand[A])(f: A => B): Rand[B] = { flatMap(ra){ a => unit(f(a)) } } def map2ByFlatMap[A,B,C](ra: Rand[A], rb: Rand[B])(f: (A,B) => C): Rand[C] = { flatMap(ra){ a => map(rb) {b => f(a,b)}} } def map3ByFlatMap[A,B,C,D](ra: Rand[A], rb: Rand[B], rc: Rand[C])(f: (A,B,C) => D): Rand[D] = { flatMap(ra){ a => flatMap(rb) {b => map(rc) {c => f(a,b,c)}}} }
看,代码是不是越来越简洁了?而且仿佛进入了数学世界。我是说现在感觉编程已经变成了好像高中做数学题一样:拿到一个函数描述就开始想办法用什么其它现有的函数来解决;然后匹配一下类型,找找以前的例子,等等。。。,完全没有感觉到是在编写计算机程序。