黒猿の家:最適化のための注釈のScala(末尾再帰の最適化)

Scalaのクラスライブラリいくつかのノートには、あなたは、コンパイラの最適化を制御することができ、始まろうとして導入

1、末尾再帰@tailrec

object Module_WeiDG {
  @tailrec 
  def story(): Unit = {
    println("从前有座山,山上有座庙,庙里有个老和尚,一天老和尚对小和尚讲故事")
    story()
  }
}

尖叫提示:进入下一个函数,不再需要上一个函数的环境了,得出结果以后直接返回。尾递归调用,可以被转化成循环,这样能节约栈空间

2、非末尾再帰

def story(): Unit = { 
  println("从前有座山,山上有座庙,庙里有个老和尚,一天老和尚对小和尚讲故事")
  story()
  println("小和尚听了,找了一块豆腐撞死了")
}

尖叫提示:下一个函数结束以后,此函数还有后续,所以必须保存本身的环境以供处理返回值。

3、末尾再帰の最適化ケース

スタックは、スペースを節約することができるようにそれは、再帰呼び出しサイクルに変換することができます。関数型プログラミングでは、非常に重要である、私たちはしばしば、コレクションをトラバースする再帰的なメソッドを使用します

(1)非末尾再帰

object Util {
    def sum(xs: Seq[Int]): BigInt = {
        if (xs.isEmpty) 0 else xs.head + sum(xs.tail)
    }
        ...
}

計算プロセスは、最後のステップはまた、ない再帰呼び出しであるため、上記の和方法は、最適化することができません。調整後のコード

(2)末尾再帰の最適化

def sum2(xs: Seq[Int], partial: BigInt): BigInt = {
    if (xs.isEmpty) partial else sum2(xs.tail, xs.head + partial)
}

Scalaのコンパイラが自動的にアプリケーションSUM2「末尾再帰」を最適化します あなたは合計(1〜1000000)を呼び出した場合、スタックオーバーフローエラーが発生します。しかし、SUM2(1000000から1、0)は、正しい結果を取得します。

Scalaのコンパイラは末尾再帰の最適化を使用しようとしますが、コンパイラが最適化することはできませんエラーをしたい場合は、時にはあまり明白な理由のいくつかは、それは、そのようなより複雑なロジックとして、そうすることができない理由が、あなたが与える必要があり、あなたのコンパイラは、アプリケーションを最適化することができない場合、それはエラーになりますように、方法@tailrecコメントを追加

(3)トランポリン

トランポリンは、興味は自分自身を理解することについての詳細を学ぶことができます

import scala.util.control.TailCalls._
def evenLength(xs: Seq[Int]): TailRec[Boolean] = {
  if(xs.isEmpty) done(true) else tailcall(oddLength(xs.tail))
}

def oddLength(xs: Seq[Int]): TailRec[Boolean] = {
  if(xs.isEmpty) done(false) else tailcall(evenLength(xs.tail))
}

// 获得TailRec对象获取最终结果,可以用result方法
evenLength(1 to 1000000).result

尖叫提示:对于消除递归,一个更加通用的机制叫做“蹦床”。蹦床的实现会将执行一个循环,不停的调用函数。每个函数都返回下一个将被调用的函数。尾递归在这里是一个特例,每个函数都返回它自己。

Scala有一个名为TailCalls的工具对象,帮助我们轻松实现蹦床。相互递归的函数返回类型为TailRec[A],其要么返回done(result),要么返回tailcall(fun)。其中,fun是下一个被调用的函数。这必须是一个不带额外参数且同样返回TailRec[A]的函数上面是一个简单的示例

ます。https://www.jianshu.com/p/b1d09316ccd6で再現

おすすめ

転載: blog.csdn.net/weixin_33933118/article/details/91182520