模拟状态机消除递归心得

在之前的“求字符串最长不连续回文序列的深入研究“文章里我使用了性能欠佳的递归来解决问题,后为了达到消除递归的目的在网上看到了使用模拟状态机方法消除,感觉很不错,学习之后在这里进行一下总结。

其实这个方法很简单,主要步骤如下:

  1. 写出递归函数
  2. 根据递归函数画出状态机流程图,除了起止节点,将其他节点标序号
  3. 根据状态机创建Context类
  4. 定义状态机方法

首先介绍一下Context对象:

状态机中,会充斥着各种各样的条件(C),每个条件对应Context的一个返回boolean类型的方法,另外递归函数中同时充斥着上下文相关的变量(就是jvm局部变量表用到的那些),这些变量都要放到Context里作为成员变量。
Context还需要有parent引用,用来模拟出栈入栈(当然可以用真正的栈,就不需要parent引用了),还需要一个baby方法,返回新的Context对象并将this赋予新的parent,用来模拟入栈(同样也可以使用真正的栈来替代)

接下来举几个例子来说明如何使用这种方法消除递归。
递归求阶乘
1.写出递归函数

def f( n : Int ) : Int = {
    if( n == 1 ) 1
    else {
      val t = f( n - 1 )
      t * n
    }
  }

2.根据递归函数画出状态机流程图,并标号
这里写图片描述
其实状态图很简单,就是找条件找条件找条件。。。遇到递归就入栈,遇到return就判断栈空

上图中:
状态1.C1对应代码中的n==1
状态2.C2对应栈空(用对应于Context的parent==null)
Act1对应于t * n,也就是出栈拿到子上下文结果t后,使用t做一些事情
状态3.上下文入栈(包装)对应于调用f(n-1),在这里创建新Context对象
状态4.上下文出栈(拆装)用来模拟出栈返回结果

3.根据状态机创建Context类:

case class Context(parent : Context , n : Int , var result : Int){
    def c1() = n == 1
    def c2() = parent == null
    def act1(resultFromChild : Int) = result = resultFromChild * n
    def baby : Context = Context( this , n - 1 , 1 )
}

4.根据状态机创建状态机方法:

def stateMachine( _ctx: Context ): Int = {
    var (ctx, stateNum) = (_ctx , 1 )

    while( stateNum != -1 ){//-1 退出
      stateNum match {
        case 1 => stateNum = if( ctx.c1 ) 2 else 3
        case 2 => stateNum = if( ctx.c2 ) -1 else 4
        case 3 =>
          ctx = ctx.baby
          stateNum = 1
        case 4 =>
          ctx.parent.act1( ctx.result )
          ctx = ctx.parent
          stateNum = 2
        case _ =>
      }
    }
    ctx.result
}

6.客户端调用:

def main(args: Array[String]): Unit = {
  println( stateMachine( Context( null , 10, 1 ) ) )
}

下面看一个复杂一点的,快速排序:
1.递归函数:

def sort( low : Int , high : Int , arr : Array[Int] ) : Unit ={
    var ( l , h , p ) = ( low , high , arr( low ) )

    while( l < h ){
      while ( l < h && arr( h ) >= p ) h -= 1
      if( l < h ) swap( l , h , arr )

      while( l < h && arr( l ) <= p ) l += 1
      if( l < h ) swap( l , h , arr )
    }

    if( l > low ) sort( low , l - 1 , arr )
    if( h < high ) sort( l + 1 , high , arr )
}

def swap(i: Int, j: Int, arr: Array[Int]) = {
    val t = arr(i)
    arr(i) = arr(j)
    arr(j) = t
}

可以看到在sort里面两处调用递归,这个时候在出栈处理的时候有些不同
2.画出状态图:
这里写图片描述
在有多处调用递归的时候,Context需要新加一个callsite成员,这样恢复上下文后,调用ctx的route方法判断下一状态是多少
3.创建Context类:

case class Context(
                    var parent : Context,
                    var l : Int , var h : Int,
                    var low : Int , var high : Int,
                    var callsite : Int
                    )
  {
    override def clone(): AnyRef = Context(parent,low,high,low,high,callsite)

    //条件,c1,c2,c3...太多,这里写到一起
    def c(i : Int) : Boolean = i match {
      case 1 => l < h
      case 2 => c( 1 ) && arr( h ) >= arr( low )
      case 3 => c( 1 )
      case 4 => c( 1 ) && arr( l ) <= arr( low )
      case 5 => c( 1 )
      case 6 => l > low
      case 7 => h < high
      case 8 => parent == null
    }

    //同理,写到一起
    def act(i : Int) : Unit = i match {
      case 1 => h -= 1
      case 2 => swap( l , h , arr )
      case 3 => l += 1
      case 4 => act( 2 )
    }

    def route = callsite match {
      case 6 => 7
      case 7 => 8
    }

    def baby(callsite : Int) : Context= {
      val res = this.clone.asInstanceOf[Context]
      res callsite_= callsite
      res parent_= this

      callsite match {
        case 6 =>
          res high_= l - 1
          res h_= l - 1
        case 7 =>
          res low_= l + 1
          res l_= l + 1
      }
      res
    }

  }

4.创建状态机

def stateMachine( _ctx: Context ): Unit = {
    var (ctx, nextState , callsite) = (_ctx , 1 , 0)

    //如果满足,则转到ify状态,否则转到ifn状态
    def ~~ ( ynum : Int , nnum: Int , ify : => Any = None , ifn : => Any = None ) ={
      val y = ctx c nextState
      if( y ) ify else ifn
      nextState = if( y ) ynum else nnum
    }
    while( nextState != -1 ){//-1 退出
      nextState match {
        case 1 => ~~ ( 2 , 6 )
        case 2 => ~~ ( 2 , 3 , ctx act 1 )
        case 3 => ~~ ( 4 , 4 , ctx act 2 )
        case 4 => ~~ ( 4 , 5 , ctx act 3 )
        case 5 => ~~ ( 1 , 1 , ctx act 4 )
        case 6 => ~~ ( 10 , 7 , callsite = 6)
        case 7 => ~~ ( 10 , 8 , callsite = 7)
        case 8 => ~~ ( -1 , 9 )
        case 9 =>
          nextState = ctx.route
          ctx = ctx.parent
        case 10 =>
          ctx = ctx baby callsite
          nextState = 1

        case _ =>
      }
    }
}

5.客户端调用

var arr : Array[Int] = _
def main(args: Array[String]): Unit = {
    arr = Array(2,3,5,1,6)
    stateMachine( Context(null , 0 , arr.length - 1 , 0 , arr.length - 1 , 0) )
    println( arr toList )//List(1,2,3,5,6)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值