scala関数式プログラミングノート:純関数式状態

5657 ワード

scala関数式プログラミング:純関数式状態読書ノート
Overview:
  • ステータス付きメソッドの宣言式実装は副作用がある可能性があり、参照の透明性を保つことは困難である.
  • 純粋な関数式で状態付き関数を実現する鍵は,状態更新を明示的にし,副作用的に状態を更新するのではなく,生成した値とともに新しい状態を返すことにある.すなわち,状態の暗黙的な変更を暴露する.
  • 例:関数nextIntは変更後の新状態nextRNGを返す
  • trait RNG{
      def nextInt: (Int,RNG)
    }
    
    case class SimpleRNG(seed: Long) extends RNG{
      //      Int ,      ,    SimpleRNG
      def nextInt: (Int,RNG) = {
        val newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFL
        val nextRNG = SimpleRNG(newSeed)
        val n = (newSeed >>> 16).toInt
    
        (n, nextRNG)
      }
      
      //        ,currentSeed       
      val currentSeed = seed
      def nextInt2: Int = {
        val newSeed = (currentTime * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFL
        currentSeed = newSeed
        val n = (newSeed >>> 16).toInt
    
        n
      }
    }
    

    コンボ:
  • アセンブリは高次関数であり、表示されないように状態伝達を行うための関数のタイプ別名と見なすことができる.
  • RNG=>(A,RNG)は、古いRNGを受け入れて新しいRNGを得る関数であり、状態挙動は、組合せ子Rand:
  • type Rand[+A] = RNG => (A, RNG)
    
  • 実例分析:
  • val int: Rand[Int] = _.nextInt
  • _.nextInt RNG => (Int,RNG) .すなわち、RNGタイプパラメータを受け入れ、(Int,RNG)タイプメタグループの関数を返す.
  • そしてRand[Int]は関数RNG => (Int,RNG)のタイプ別名です.


  • 状態の動作を組み合わせるには、コンボを使用します.
  • 状態のある関数については、まずそのパラメータと戻り値を組み合わせ子としてパッケージする.そしてmap,flatMap等により純粋な関数として実現する.ここでf,gなどがこの関数である.
  • 状態挙動:s(rng 1:RNG)戻り(v:Int,rng 2:RNG)
  • def map[A,B](s: Rand[A])(f: A => B): Rand[B] = 
        rng1 => {
            val (v,rng2) = s(rng1)
            (f(v),rng2)
        }
        
    def flatMap[A,B](f: Rand[A)(g: A => Rand[B]): Rand[B] = 
        rng => {
            (v,r) = f(rng)
            g(v)(r)
        }
    
  • 2つの状態行動を組み合わせる:
  • def map2[A,B,C](ra: Rand[A], rb: Rand[B])(f: (A,B) => C): Rand[c] = 
        rng1 => {
            val (v1,rng2) = ra(rng1)
            val (v2,rng3) = rb(rng2)
            (f(v1,v2),rng3)
        }
    
  • シーケンス状態挙動の組み合わせ:
  • def unit[A](a: A): Rand[A] = rng => (a,rng)
    
    def sequence[A](fs: List[Rand[A]]): Rand[List[A]] =
        List.foldRight(fs,unit(List[A())))((x,y) => map2(x,y)((a,b) => Cons(a,b)))
    

    汎用状態動作データ型
  • 前合成子状態がタイプRNGである.コンビネーションを汎化しましょう.Sは任意の状態を表す.
  • type State[+A,S] = S => (A,S)
    
  • 汎化した組合せ子を1つのclassと定義することもできる.State[+A,S]で定義された関数は、関数スタイルの任意のステートマシンまたはステータスプログラムを実現するために必要なすべてのツールです.
  • case class State[+A,S](run: S => (A,S)) extends AnyVal{
      
      def map[B](f: A => B): State[B,S] = State(
        s => {
          val (i,r) = run(s)
          (f(i),r)
        })
    
      def map2[B,C](b: State[B,S])(f: (A,B) => C): State[C,S] = State(
        s => {
          val (v1,s1) = run(s)
          val (v2,s2) = b.run(s1)
          (f(v1,v2),s2)
        })
    
      //def _map2[B,C](b: State[B,S])(f: (A,B) => C): State[C,S] =
      //  flatMap(x => b.map(y => f((x,y))))
    
    
      def flatMap[B](f: A => State[B,S]): State[B,S] = State(
        s => {
          val (v,s1) = run(s)
          f(v).run(s1)
        })
    }
    
    object State{
    
      def unit[A,S](x: A): State[A,S] = State(s => (x,s))
      def sequence[A,S](ls: List[State[A,S]]): State[List[A],S] =
        List.foldRight(ls,unit[List[A],S](List()))((x,y) => x.map2(y)(Cons(_,_)))
    
      def modify[S](f: S => S): State[Unit,S] = for {
        s  (s,s))
      def set[S](s: S): State[Unit,S] = State(_ => ((), s))
    }
    
    

    インスタンスの適用
  • タイトル
  • EXERCISE 13 (hard): To gain experience with the use of State, implement a simulation of a simple candy dispenser. The machine has two types of input: You can insert a coin, or you can turn the knob to dispense candy. It can be in one of two states: locked or unlocked. It also tracks how many candies are left and how many coins it contains.
    [外部チェーン画像の転送に失敗しました(img-Amx 9 zUg-1564306192450)(http://oxaz2p2ac.bkt.clouddn.com/Screen Shot 2017-10-04 at 10.21.02 PM.png)]
  • 標準回答:https://github.com/fpinscala/fpinscala/blob/master/answerkey/state/11.answer.scala
  • 私の解法:https://github.com/haiboself/Scala-learning/tree/master/src/PureFunctionalState
  • 参考資料:
  • https://stackoverflow.com/questions/41012375/functional-programing-in-scala-simulatemachine/41328969#41328969
  • https://groups.google.com/forum/#!topic/scala-functional/8iHaSFPPjgw

  • package PureFunctionalState
    
    import datastruct._
    
    sealed trait Input
    case object Coin extends Input
    case object Turn extends Input
    
    case class Machine(locked: Boolean, candies: Int, coins: Int) {
    
      
      /**        
        *    Machine          ,       Machine          
        *                    
        */
      def simulateMachine(inputs: List[Input]): Machine = {
        List.foldLeft(inputs,Machine(locked,candies,coins))((x, y) => (x,y) match {
          case (Coin,Machine(true,cands,x)) if cands>0 => Machine(false,cands,x)
          case (Turn,Machine(false,cands,x)) if cands>0 => Machine(true,cands-1,x+1)
          case _ => y
        }
        )
      }
      
      /**       。
        * modify  State[Unit,Machine]。modify         ,    inputs           Machine
        * map  List[State[Unit,Machine]]
        * sequence  State[List[Unit],Machine]
        */
      def simulateMachine3(inputs: List[Input]): State[(Int, Int),Machine] = for {
        _  State.modify((s: Machine) => (i, s) match {
          case (_, Machine(_, 0, _)) => s
          case (Coin, Machine(false, _, _)) => s
          case (Turn, Machine(true, _, _)) => s
          case (Coin, Machine(true, candy, coin)) =>
            Machine(false, candy, coin + 1)
          case (Turn, Machine(false, candy, coin)) =>
            Machine(true, candy - 1, coin)
        })))
        s