尾递归消除

尾调用

使用循环方式
  def factorial(n: Int): Int = {
    def go(n: Int, acc: Int): Int = {
      if (n <= 0) acc
      else go(n - 1, n * acc)
    }
    go(n, 1)
  }

想不通过修改一个循环变量而实现循环功能,可以借助递归函数。我们在阶乘函数内部定义了一个辅助的递归函数。

这种辅助函数习惯上被称为go或loop。在Scala中函数可以定义在任何代码块中,包括在另一个函数内部。就像一个局部变量,go函数只能被factorial函数内部引用。factorial函数的定义最终不过只是以循环的初始条件不断地调用go。

传给go的参数是循环的状态。子啊这个例子中包含n和一个当前阶乘的累计值acc。为进行下一次迭代,只需用新的循环转态递归调用go函数(这里是go(n - 1, n * acc)),要退出循环,返回一个不继续进行递归调用的值(这里是n <= 0)。Scala会检测到这种自递归,只要递归调用发生在尾部,编译器优化成类似while循环的字节码。

尾调用消除

我们说的尾调用是指调用者在一个递归调用之后不做任何事,只是返回这个调用结果。另一种情况:1 + go(n-1, n*acc),这里go不再是尾部,因为这个方法的结果还要参与其他运算。

如果递归调用实在一个函数的尾部位置,Scala会自动把递归编译为循环迭代,这样不会每次都进行栈的操作。

默认情况下Scala不会告诉你尾调用是否消除成功,可以通过tailrec注释来告诉编译器,如果编译不能消除尾部调用会给出编译错误。

import scala.annotation.tailrec

object Tailrec{
  def factorial(n: Int): Int = {
    @tailrec
    def go(n: Int, acc: Int): Int = {
      if (n <= 0) acc
      else go(n - 1, n * acc)
    }
    go(n, 1)
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值