Scala 函数式编程 https://github.com/fpinscala/fpinscala
java里面产生随机数
scala> val rng = new java.util.Random
rng: java.util.Random = java.util.Random@caca6c9
scala> rng.nextDouble
res1: Double = 0.9867076608154569
scala> rng.nextDouble
res2: Double = 0.8455696498024141
scala> rng.nextInt
res3: Int = -623297295
scala> rng.nextInt
res4: Int = 1989531047
每次nextInt
都会产生不同的随机数, 不用拆开java.util.Random
来看, 就可以猜到,里面一定有field在变化, 不然每次产生的数就不可能达到不同
但是这每次调用都产生不同的数, 就不是引用透明了, 不是纯函数了, 每次产生nextInt
都会更新java.util.Random
里面的状态, 这就是副作用
改造成纯函数: 返回本类的另一个新实例
只要让状态不在对象内部改变, 而是显式地改变就行了, 就是
trait RNG {
def nextInt: (Int, RNG)
}
老的RNG
对象还是不变, 返回一个新的RNG
对象
这样仍然封装了状态, 调用者仍然不需要知道具体细节
使用:
def randomPair(rng: RNG): ((Int,Int), RNG) = {
val (i1,rng2) = rng.nextInt
val (i2,rng3) = rng2.nextInt// 注意用的RNG 是上一次nextInt产生的
((i1,i2), rng3)
}
另一个例子:
原来有状态的类, 假设这2函数都会改变这个s
class Foo {
var s: FooState = ...// 状态
def bar: Bar
def baz: Int
}
改造成不可变类:
trait Foo {
def bar: (Bar, Foo)// 返回另一个Foo
def baz: (Int, Foo)// 返回另一个Foo
}
当然, 这样频繁产生新对象, 会有效率的损失, 但是,可以用高效的纯函数数据结构来缓解
组合 state action
产生随机正数
def nonNegativeInt(rng: RNG): (Int, RNG) = {
val (i, r) = rng.nextInt
val int =
if (i < 0) {
-(i + 1)
}else{
i
}
(int, r)
}
产生[0,1)随机double
def double(rng: RNG): (Double, RNG) = {
val (i, r) = nonNegativeInt(rng)
(i / (Int.MaxValue.toDouble + 1), r)
}
产生随机(Int,Double)
def intDouble(rng: RNG): ((Int, Double), RNG) = {
val (i, r1) = rng.nextInt
val (d, r2) = double(r1)
((i, d), r2)
}
可以看到 , 以上的函数类型有规律 都是 RNG => (某类型, RNG)
这种函数类型 描述了 __state actions __ 转变RNG的状态
可以创造一个type alias 来表示 如上的随机数产生函数
type Rand[+A] = RNG => (A, RNG)
产生随机整数的函数就可以表示为
val int: Rand[Int] = _.nextInt
这样提升了抽象程度
map给随机数产生函数加转换, 像装饰者模式
/**
*
* @param s 原随机生成器
* @param f 套上的函数
* @return 套上f的随机生成器
*/
def map[A,B](s: Rand[A])(f: A => B): Rand[B] =
rng => {
val (a, rng2) = s(rng)
(f(a), rng2)
}
上面的nonNegativeInt和double可以改写为
val nonNegativeInt: Rand[Int]= map(int)(i=>{if (i < 0) -(i + 1) else i})
val double: Rand[Double] = map(nonNegativeInt)(_ / (Int.MaxValue.toDouble + 1))
函数的组合 map2
2个Rand 合成一个:
/**
*
* @param ra RNG =>(A,RNG)
* @param rb RNG =>(B,RNG)
* @param f (A, B) => C 上面2者产生的结果怎么结合
* @return 随机生成器 产生C
*/
def map2[A,B,C](ra: Rand[A], rb: Rand[B])(f: (A, B) => C): Rand[C] = {
rng => {
val (a,r1) = ra(rng)
val (b,_) = rb(rng)
( f(a,b),r1)
}
}
产生Int Doubel对的函数 原本写成
def intDouble(rng: RNG): ((Int,Double), RNG) = {
val (i1, r1) = rng.nextInt
val (i2, r2) = double(r1)
((i1,i2),r2)
}
用map2可以写成
def intDouble2(rng: RNG): ((Int,Double), RNG) = map2(int,double)((a:Int,b:Double)=>(a,b))(rng)
flatMap:
def flatMap[A,B](f: Rand[A])(g: A => Rand[B]): Rand[B] =
rng => {
val (a, r1) = f(rng)
g(a)(r1) // We pass the new state along
}
这是最基本的 map和map2可以用它实现
def unit[A](a: A): Rand[A] =
rng => (a, rng)
def _map[A,B](s: Rand[A])(f: A => B): Rand[B] =
flatMap(s)(a => unit(f(a)))
def _map2[A,B,C](ra: Rand[A], rb: Rand[B])(f: (A, B) => C): Rand[C] =
flatMap(ra)(a => map(rb)(b => f(a, b)))
map map2 flatMap 在 list option里面都有, 实现不同 但是签名都是一个格式 , 都是外部一个类型 套着里面一个类型,不变外面类型操作里面的值
去掉RNG 更加通用
上面的函数 和RNG 本身的行为已经没多少关系了
只要是S => (A, S)
这样的形式 都是一样的
可以写得更加通用
case class State[S, +A](run: S => (A, S)) {
def map[B](f: A => B): State[S, B] =
flatMap(a => unit(f(a)))
def map2[B,C](sb: State[S, B])(f: (A, B) => C): State[S, C] =
flatMap(a => sb.map(b => f(a, b)))
def flatMap[B](f: A => State[S, B]): State[S, B] = State(s => {
val (a, s1) = run(s)
f(a).run(s1)
})
}
object State {
def unit[S, A](a: A): State[S, A] =
State(s => (a, s))
def modify[S](f: S => S): State[S, Unit] = for {
s <- get // Gets the current state and assigns it to `s`.
_ <- set(f(s)) // Sets the new state to `f` applied to `s`.
} yield ()
def get[S]: State[S, S] = State(s => (s, s))
def set[S](s: S): State[S, Unit] = State(_ => ((), s))
///.....
}
用for表达式更加简单(实现了map和flatMap的对象都可以用)
type Rand[A] = State[RNG, A]
val int: Rand[Int] = State(s=>{s.nextInt})
// A simple recursive solution
def ints(count: Int): Rand[ List[Int]] = State(s=>{
@scala.annotation.tailrec
def go(count: Int, r: RNG, xs: List[Int]): (List[Int], RNG) =
if (count == 0)
(xs, r)
else {
val (x, r2) = r.nextInt
go(count - 1, r2, x :: xs)
}
go(count, s, List())
})
def main(args: Array[String]): Unit = {
val a:State[RNG,List[Int]] = int.flatMap(x =>
int.flatMap(y =>
ints(x).map(xs =>
xs.map(_ % y))))
}
for {
x <- int
y <- int
xs <- ints(x)
} yield xs.map(_ % y)
状态组合子:
- unit
- map
- flatMap
和set get 是实现函数式分割的任何 状态机 或 带状态程序 的全部工具
最后 get set modify 干啥用的 没看懂
网友评论