【问题标题】:How to ensure tail recursion consistently如何确保尾递归一致
【发布时间】:2014-09-13 13:59:50
【问题描述】:

我想让一些函数针对尾递归进行优化。该函数会在没有优化的情况下发出 stackoverflow 异常。

示例代码:

import scala.util.Try
import scala.annotation.tailrec

object Main {
  val trials = 10

  @tailrec
  val gcd : (Int, Int) => Int = {
    case (a,b) if (a == b) => a
    case (a,b) if (a > b) => gcd (a-b,b)
    case (a,b) if (b > a) => gcd (a, b-a)
  }

  def main(args : Array[String]) : Unit = {
    testTailRec()
  }

  def testTailRec() {
    val outputs : List[Boolean] = Range(0, trials).toList.map(_ + 6000) map { x =>
      Try( gcd(x, 1) ).toOption.isDefined
    }
    outputTestResult(outputs)
  }

  def outputTestResult(source : List[Boolean]) = {
    val failed = source.count(_ == false)
    val initial = source.takeWhile(_ == false).length
    println( s"totally $failed failures, $initial of which at the beginning")
  }
}

运行它将产生以下输出:

[info] Running Main 
[info] totally 2 failures, 2 of which at the beginning

因此,前两次运行没有进行优化,并且由于 stackoveflow 异常而中途放弃,只有后面的调用才会产生所需的结果。

有一个解决方法:在实际使用之前,您需要通过假运行来预热该功能。但这似乎很笨拙且非常不方便。是否有任何其他方法可以确保我的递归函数在第一次运行之前针对尾递归进行优化?

更新:

有人告诉我使用两步定义

@tailrec
def gcd_worker(a: Int, b: Int): Int = {
      if (a == b) a
      else if (a > b) gcd(a-b,b)
         else gcd(a, b-a)
}
val gcd : (Int,Int) => Int = gcd_worker(_,_)

如果可能的话,我更喜欢保持简洁的函数式定义。

【问题讨论】:

标签: scala tail-recursion


【解决方案1】:

我认为@tailrec 根本不适用于定义为val 的函数。将其更改为def,它将正常运行。

【讨论】:

【解决方案2】:

据我了解,@tailrec[1] 需要在方法上,而不是在字段上。通过进行以下更改,我能够使其在 REPL 中成为尾递归:

@tailrec
def gcd(a: Int, b: Int): Int = {
      if (a == b) a
      else if (a > b) gcd(a-b,b)
      else gcd(a, b-a)
}

[1]http://www.scala-lang.org/api/current/index.html#scala.annotation.tailrec

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-21
    • 2018-03-17
    • 1970-01-01
    • 2017-11-11
    • 1970-01-01
    • 1970-01-01
    • 2023-01-13
    相关资源
    最近更新 更多