美文网首页
Chapter 6: Purely functional sta

Chapter 6: Purely functional sta

作者: 胖达_4b7e | 来源:发表于2019-05-22 22:13 被阅读0次

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 干啥用的 没看懂

相关文章

网友评论

      本文标题:Chapter 6: Purely functional sta

      本文链接:https://www.haomeiwen.com/subject/xnsrzqtx.html