美文网首页
纯函数式状态(1)

纯函数式状态(1)

作者: 吐思圈 | 来源:发表于2018-03-04 22:58 被阅读0次

    想让状态更新恢复引用透明的关键是让状态更新是显示的。不要以副作用方式更新状态,而是连同生成值一起返回一个新的状态。
    纯函数式随机数生成器:

    trait RNG {
    
      def nextInt: (Int, RNG)
    }
    
    case class SimpleRNG(seed: Long) extends RNG {
      override def nextInt: (Int, RNG) = {
        val newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL
        val newRNG = SimpleRNG(newSeed)
        val n = (newSeed >>> 16).toInt
        (n, newRNG)
      }
    }
    

    练习 6.1
    写一个函数使用RNG.nextInt生成0和Int.MaxValue之间(含)的随机数。

      def nonNegativeInt(rng: RNG): (Int, RNG) = {
        val (v, rng1) = rng.nextInt
        if (v == Int.MaxValue) (0, rng1)
        else (Math.abs(v), rng1)
      }
    

    练习 6.2
    写一个函数生成0到1之间的Double数。

      def double(rng: RNG): (Double, RNG) = {
        val (i, rng1) = nonNegativeInt(rng)
        if (i == Int.MaxValue) (0.0, rng1)
        else (i.toDouble / Int.MaxValue, rng1)
      }
    

    练习 6.3
    写一个函数生成一个(Int, Double)对,一个(Double, Int)对和一个(Double, Double, Double)三元组。

      def intDouble(rng: RNG): ((Int, Double), RNG) = {
        val (i1, rng1) = rng.nextInt
        val (i2, rng2) = rng1.nextInt
        ((i1, i2.toDouble), rng2)
      }
      
      def doubleInt(rng: RNG): ((Double, Int), RNG) = {
        val ((i, d), rng1) = intDouble(rng)
        ((d, i), rng1)
      }
      
      def double3(rng: RNG): ((Double, Double, Double), RNG) = {
        val ((_, d1), rng1) = intDouble(rng)
        val ((_, d2), rng2) = intDouble(rng1)
        val ((_, d3), rng3) = intDouble(rng2)
        ((d1, d2, d3), rng3)
      }
    

    练习 6.4
    写一个函数生成一组随机数。

      def ints(count: Int)(rng: RNG): (List[Int], RNG) = {
        def loop(count: Int, res: (List[Int], RNG)): (List[Int], RNG) = count match {
          case 0 => res
          case _ =>
            val (li, rng) = res
            val (i, rng1) = rng.nextInt
            loop(count - 1, (i :: li, rng1))
        }
        loop(count, (Nil, rng))
      }
    

    状态行为更好的API
    回顾我们上述练习的实现,会注意到一个通用的模式:我们每个函数都有一个形式为RNG => (A, RNG)的类型。这种类型的函数被称为状态行为(state action)或状态转移。状态行为可以通过组合子来组合,组合子是一个高阶函数。既然需要一只乏味地重复传递状态参数,何不用组合子帮我们在行为之间自动传递。为了便于学习讨论,我们对RNG状态行为数据类型定义一个类型别名:

      type Rand[+A] = RNG => (A, RNG)
    
      val int: Rand[Int] = _.nextInt
    
      def unit[A](a: A): Rand[A] =
        rng => (a, rng)
    
      def map[A, B](s: Rand[A])(f: A => B): Rand[B] =
        rng => {
          val (a, rng1) = s(rng)
          (f(a), rng1)
        }
      
      def nonNegativeEven: Rand[Int] =
        map(nonNegativeInt)(i => i - i % 2)
    

    练习 6.5
    使用map以更为优雅的方式重新实现double函数,参见练习6.2。

      def double1(rng: RNG): (Double, RNG) =
        map(nonNegativeInt){i =>
          if (i == Int.MaxValue) 0.0 else i.toDouble / Int.MaxValue
        }(rng)
    

    组合状态行为
    练习 6.6
    按照下面的函数签名写一个map2函数。这个函数接受两个行为,ra和rb,以及一个函数f用于组合它们的结果,并返回一个组合了两者的新行为。

      def map2[A, B, C](ra: Rand[A], rb: Rand[B])(f: (A, B) => C): Rand[C] =
        rng => {
          val (a, rng1) = ra(rng)
          val (b, rng2) = rb(rng1)
          (f(a, b), rng2)
        }
    

    练习 利用map2组合子重新实现intDouble和doubleInt函数

      def both[A, B](ra: Rand[A], rb: Rand[B]): Rand[(A, B)] =
        map2(ra, rb)((_, _))
      
      val int: Rand[Int] = _.nextInt
    
      val doubles: Rand[Double] =
        rng => {
          val (i, rng1) = rng.nextInt
          (i.toDouble, rng1)
        }  
    
      def intDouble1(rng: RNG): ((Int, Double), RNG) =
        both(int, doubles)(rng)
    
      def doubleInt1(rng: RNG): ((Double, Int), RNG) =
        both(doubles, int)(rng)
    

    练习 6.7
    如果你能组合两个RNG转换行为,那么同样也可以组合一个行为列表。实现sequence函数将一个转换行为列表组合成一个转换行为。使用它实现之前写过的ints函数。

      def sequence[A](fs: List[Rand[A]]): Rand[List[A]] =
        rng => {
          def loop(n: Int, res: (List[A], RNG)): (List[A], RNG) = n match {
            case m if m < 0 => res
            case _ => 
              val (li, rng) = res
              val (a, rng2) = fs(n)(rng)
              loop(n - 1, (a :: li, rng))
          }
          loop(fs.length - 1, (Nil, rng))
        }
    
      def ints1(count: Int)(rng: RNG): (List[Int], RNG) =
        sequence(List.fill(count)(int))(rng)
    

    嵌套状态行为
    map和map2组合子允许我们以特别简洁优雅的方式实现,其它实现方式则显得乏味且容易产生错误,但是所有的函数都可以使用map和map2组合子实现,有时候我们需要连续传递RNG,这时最好的方式使用一个新的组合子帮我们传递,所以我们需要一个更强大的组合子flatMap。
    练习 6.8
    实现flatMap然后用它实现nonNegativeLessThan。

      def flatMap[A, B](ra: Rand[A])(f: A => Rand[B]): Rand[B] =
        rng => {
          val (a, rng1) = ra(rng1)
          f(a)(rng1)
        }
    

    练习 6.9
    根据flatMap重新实现map, map2。这也是flatMap比map和map2更强大的地方。

      def map1[A, B](ra: Rand[A])(f: A => B): Rand[B] =
        flatMap(ra)(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)
          }
        }
    

    相关文章

      网友评论

          本文标题:纯函数式状态(1)

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