美文网首页
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