泛函编制程序(15)-泛函状态-随便数发生器

对于OOP程序猿来讲,泛函状态变迁(functional state
transition)是一个来历远远不够明了的课题。泛函状态变迁是经过泛函状态数据类型(functional
state)来兑现的。State是三个冒出在泛函编制程序里的门类。与其余数据类型同样,State一样需求我的一套泛函操作函数和整合函数(combinators),大家就要偏下章节中切磋关于State数据类型的解决方案。

在正式介绍State类型前,大家先从随机数发生器(库罗德NG: Random Number
Generator)开端,从泛函EnclaveNG的规律解析引领到State应用方案。

先看看我们耳濡目染的java
奇骏NG:

1 //java code2 val rng = new java.util.Random                    //> rng  : java.util.Random = java.util.Random@48533e643 4 rng.nextInt                                       //> res0: Int = -14123608695 rng.nextInt                                       //> res1: Int = 6456225206 7 rng.nextDouble                                    //> res2: Double = 0.42164778720432678 rng.nextDouble                                    //> res3: Double = 2.306856098814869E-4

其一确定不是泛函风格的TiggoNG:同一个函数每一回引用会生出差别的结果。泛函函数(pure
function)的“等量替换”在那边不创建。再者,大家简单想象在上述rng里一定爱惜了二个情状,每一回换代,爆发了附带影响(side
effect),那又违背了泛函纯函数(pure
function)的不产生附带影响的供给(referencial
transparency)。那么应该如何是好吧?泛函的做法注重在于用刚毅的方法来更新情形,即:不要爱戴内部景色,间接把新景色和结果一道重临,像下边那样:

1 trait RNG {2   def nextInt: 3 }

从以上办法大家着力能得出泛函状态变迁(state
transition)的样式:

一旦大家有那样个档期的顺序结构:

1 class Foo {2   var s: FooState = ...3   def bar: Bar4   def baz: Int5 }

假使 bar 和 baz 会改换Foo 的情状,那么大家应当这么设计bar, baz 函数:

1 trait Foo {2   def bar: 3   def baz: 4 }

好,那么大家索求设计泛函式的随机数爆发器陆风X8NG:

 1 trait RNG { 2     def nextInt:  3 } 4 //起始状态RNG, 种子RNG 5 case class seedRNG(seed: Long) extends RNG { 6     def nextInt:  = { 7      val seed2 = (seed*0x5DEECE66DL + 0xBL) & ((1L << 48) - 1) 8      ((seed2 >>> 16).asInstanceOf[Int], seedRNG 9     }10 }11 val rng = seedRNG(System.currentTimeMillis)       //> rng  : ch6.rng.seedRNG = seedRNG(1427201377395)12 val  = rng.nextInt                        //> i  : Int = 151606223413                                                   //| rng2  : ch6.rng.RNG = seedRNG(99356654630658)14 val  = rng2.nextInt                      //> i2  : Int = 48316550215                                                   //| rng3  : ch6.rng.RNG = seedRNG(31664734402533)16 val  = rng2.nextInt                      //> i3  : Int = 48316550217                                                   //| rng4  : ch6.rng.RNG = seedRNG(31664734402533)

函数nextInt再次回到了三个随意数及新的汉兰达NG。如若我们使用同三个奥迪Q5NG发生的结果是同一的r2==r3,恰恰显示了泛函风格。

享有种类的泛函式随意数发生器都得以从Int
CR-VNG nextInt推导出来:

 1 object RNG { 2   //值在 0.0 - 1.0 之间的Double随意数 3     def nextDouble: (Double, RNG) = { 4         val  = rng.nextInt 5         if ( i == Int.MaxValue ) (0.0, rng2) 6         else ( i.toDouble / Int.MaxValue.toDouble, rng2) 7     } 8     def nextPositiveInt:  =  { 9         val  = rng.nextInt10         if ( i == Int.MaxValue ) (Int.MaxValue, rng2)11         else (i.abs, rng2)12     }13     def nextBoolean: (Boolean, RNG) = {14         rng.nextInt match {15             case  => (i % 2 == 0, rng2)16         }17     }18     //产生一个随意tuple (Int, Double)19     def nextIntDouble: ((Int, Double), RNG) = {20         val  = nextPositiveInt21         val  = nextDouble22         ,rng3)23     }24     //产生一个随意数的n长度List25     def nextInts: (List[Int], RNG) = {26         def go(n: Int, rng: RNG, acc: List[Int]): (List[Int], RNG) = {27              if ( n <= 0 ) 28              else {29                  val  = rng.nextInt30               go(n-1,rng2,i :: acc)31              }32         }33         go(n,rng,Nil: List[Int])34     }35 }36 import RNG._37 38 val rng = seedRNG(System.currentTimeMillis)       //> rng  : ch6.rng.seedRNG = seedRNG(1427204725766)39 val  = nextDouble                   //> d  : Double = 0.609053678162886640                                                   //| rng5  : ch6.rng.RNG = seedRNG(85716684903065)41 val  = nextBoolean                 //> b  : Boolean = false42                                                   //| rng6  : ch6.rng.RNG = seedRNG(123054239736112)43 val , rng7) = nextIntDouble         //> i5  : Int = 105492465944                                                   //| d2  : Double = 0.887787577178230345                                                   //| rng7  : ch6.rng.RNG = seedRNG(124944993788778)46 val (ints, rng8) = nextInts              //> ints  : List[Int] = List(-782449771, -1992066525, -825651621, -440562357, 747                                                   //| 00809062)48                                                   //| rng8  : ch6.rng.RNG = seedRNG(230196348539649)

从以上的事例中得以窥见那一个函数一致的格局:func:,即:瑞鹰NG
=> , 是lambda
function,纯纯的函数类型表明。那样看来随便数产生器正是一个函数类型,大家能够把发生器当作函数的参数只怕重返值来利用。那么大家可以自创一个新的类型:

1     type Rand[+A] = RNG => 

Rand正是贰个随便数发生器,大家是能够把它传来传去的。那么上边大家就试着使用Rand类型:

 1 type Rand[+A] = RNG =>  2      3     def rnInt: Rand[Int] = _.nextInt 4     def rnPositiveInt: Rand[Int] = nextPositiveInt 5 } 6 import RNG._ 7  8 val rng = seedRNG(System.currentTimeMillis)       //> rng  : ch6.rng.seedRNG = seedRNG(1427239224137) 9 rnInt                                        //> res0: (Int, ch6.rng.RNG) = (-1225681606,seedRNG(201148706995232))10 rnPositiveInt                                //> res1: (Int, ch6.rng.RNG) = (1225681606,seedRNG(201148706995232))

在上述例子里大家大概注意到了部分同室操戈的东西:函数注明def rnInt: Rand[Int] 好像一贯不参数,但利用时 rnInt
是内需参数的。然而借使我们考虑 Func: 的lambda表明格局 LANDNG =>
自然就掌握了。

既然大家早已把自由数发生器产生了Rand类型,大家应当能够一本万利地对自由数发生器实行整合、变形了啊?先看贰个最宗旨的机件(combinator):

1     def unit[A]: Rand[A] = {2         rng => 3     }

乍看起来这几个unit好像没什么用,没做怎么着事。实际上unit能够说是函数组合的最大旨组件,是大大的有用的。unit唯有三个作用:把a包进高阶类Rand里,换句话说正是把低阶类A升格到高阶Rand,那样就能够和别的Rand进行重组可能采纳Rand函数自己变形了。这么些轻易的例证再度提示了从重返类型来演绎成效落成这种泛函编制程序风格:Band[A]
>>> RNG => 即:给自家一个WranglerNG笔者就能够回来贰个。

再来叁个针对Rand类型的map:

1     def map[A,B](ra: Rand[A])(f: A => B): Rand[B] = {2       rng => {3                 val  = ra4                 , rng2) 5         }

从上述函数的兑现情势能够得出map就是对多个自便数发生器的结果实行转移后照旧保留Rand的高阶类型格式。还和上一个事例一样:大家一看到再次来到类型Rand就应该及时想到
rng => {…}这种达成作风了。

举出个应用map的例证:

1     def rnDouble: Rand[Double] = { map(rnPositiveInt){ _ / (Int.MaxValue.toDouble + 1) } }2 val rng = seedRNG(System.currentTimeMillis)       //> rng  : ch6.rng.seedRNG = seedRNG(1427245671833)3 rnDouble                                     //> res0: (Double, ch6.rng.RNG) = (0.6156660546548665,seedRNG(86647294261296))

真够简洁的。下边再试着结合八个Rand:

1     def map2[A,B,C](ra: Rand[A], rb: Rand[B]) => C): Rand[C] = {2         rng => {3             val  = ra4             val  = rb5             , rng3)6         }7     }

真够简洁的。下边再试着结合五个Rand:

1     def map2[A,B,C](ra: Rand[A], rb: Rand[B]) => C): Rand[C] = {2         rng => {3             val  = ra4             val  = rb5             , rng3)6         }7     }

写多少个应用map2的例子:

 1     def rnPair[A,B](ra: Rand[A], rb: Rand[B]): Rand[] = { 2         map2{} 3     } 4     def rnIntDoublePair: Rand[(Int, Double)] = { 5         rnPair(rnInt,rnDouble) 6     } 7     def rnDoubleIntPair: Rand[(Double, Int)] = { 8         rnPair(rnDouble,rnInt) 9     }10 }11 import RNG._12 13 val rng = seedRNG(System.currentTimeMillis)       //> rng  : ch6.rng.seedRNG = seedRNG(1427246457588)14 rnIntDoublePair                              //> res0: ((Int, Double), ch6.rng.RNG) = ((-1302173232,0.355998701415956),seedR15                                                   //| NG(231372613633230))16 rnDoubleIntPair                              //> res1: ((Double, Int), ch6.rng.RNG) = ((0.6063716635107994,-764501390),seedR17                                                   //| NG(231372613633230))

既然大家能用map2来组合七个Rand,那么能或不能把一个List里面包车型地铁Rand结合成二个Rand呢?

 1     //用递归方式 2     def sequence[A](lr: List[Rand[A]]): Rand[List[A]] = { 3       rng => { 4           def go(xs: List[Rand[A]], r: RNG, acc: List[A]): (List[A], RNG) = { 5               lr match { 6                     case Nil =>  7                     case h :: t => { 8                         val  = h 9             go(t,rng2,x::acc)10                   }11               }12           }13           go(lr,rng,List14         }15     }16     //用foldRight实现17     def sequence_1[A](lr: List[Rand[A]]): Rand[List[A]] = {18        lr.foldRight(unit(Nil: List[A])) { => map2}19     }

从以上foldRight达成方式得以回味unit的功力:它把List[A]升了格,那样技能与Rand的map2类型相配。能够窥见使用了map,map2,sequence去操作Rand时,大家早已毫无理会那些凯雷德NG了,那申明大家曾经向着更加高的抽象层升高了,那也是泛函编制程序的真义所在。不过光靠map,map2,sequence就足以办成全部的事了吧?不是!想想那二个正整数随便数爆发器positiveInt,假诺用rnInt产生了Int.MinValue的话就再次再产生贰个,直到发生出贰个不为Int.MinValue的数。大家试着用map来完毕:

1 def positiveInt: Rand[Int] = {2   map(int) { i =>3     if (i != Int.MinValue) i.abs else ??4   }5 }

map的操作函数类型是f:
A => B,重复运算positiveInt重返类型是Rand[A],
不合作,大家就卡在这了。但再看看这一个难点能够用flatMap消除:因为flatMap的操作函数f:
A => Rand[B], 类型是同盟的。我们得以用unit把
i.abs升格就能够动用flatMap消除难题了。

赶紧完毕flatMap:

 1     def flatMap[A,B](ra: Rand[A])(f: A => Rand[B]): Rand[B] = { 2         rng => { 3             val  = ra 4             f 5         } 6     } 7     def positiveIntByFlatMap: Rand[Int] = { 8         flatMap { 9             a => {10                 if ( a != Int.MinValue) unit11                 else positiveIntByFlatMap12             }13         }14     }

没有错,能够用flatMap完结positiveInt了。那么用flatMap能够兑现map,map2吗?看看上边包车型地铁切实落到实处情势:

1   def mapByFlatMap[A,B](ra: Rand[A])(f: A => B): Rand[B] = {2       flatMap{ a => unit }3   }4   def map2ByFlatMap[A,B,C](ra: Rand[A], rb: Rand[B]) => C): Rand[C] = {5         flatMap{ a => map {b => f}}6   }7   def map3ByFlatMap[A,B,C,D](ra: Rand[A], rb: Rand[B], rc: Rand[C])(f:  => D): Rand[D] = {8         flatMap{ a => flatMap {b => map {c => f}}}9   }

看,代码是还是不是特别轻巧了?并且周边步向了数学世界。作者是说现在觉获得编制程序已经化为了类似高级中学做数学题一样:得到多个函数描述就从头想方法用怎么着其余现存的函数来消除;然后匹配一下品种,找找以往的事情例,等等。。。,完全未有认为到是在编辑计算机程序。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图