美文网首页
scala尾递归

scala尾递归

作者: lijiaccy | 来源:发表于2020-04-13 23:05 被阅读0次

什么是递归

一个函数直接或者间接的调用本身,这就是递归。典型案例斐波那契数列

def fib(n: Int): Int = {
  if (n == 1)  0
  else if (n == 2)  1
  else {
    fib(n - 1) + fib(n - 2)
  }
 }

以上的方法中当n>3时,就是典型的递归了。
递归一般符合人们的思维模式,但是缺点就是

1、递归由于是函数调用自身,而函数调用是有时间和空间的消耗的
2、递归中很多计算都是重复的,由于其本质是把一个问题分解成两个或者多个小问题,多个小问题存在相互重叠的部分,则存在重复计算
3、调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。

针对于上面三点,尾递归就出现了

尾递归

尾递归是指递归调用是函数的最后一个语句,而且其结果被直接返回。由于递归结果总是直接返回,尾递归比较方便转换为循环,因此编译器容易对它进行优化。
针对上面的斐波那契改成尾递归

def fib(n: Int): Int = {
  @tailrec
  def _loop(n: Int, acc1: Int, acc2: Int): Int =
    if(n <= 2)
      acc2
     else
       _loop(n-1, acc2, acc1+acc2)

   _loop(n, 1, 1)
}

尾递归版本最重要的就是找到合适的累加器,该累加器可以保留最后一次递归调用留在堆栈中的数据,积累之前调用的结果,这样堆栈数据就可以被丢弃,当前的函数栈可以被重复利用
在这个例子中,变量acc就是累加器,每次递归调用都会更新该变量,直到递归边界条件满足时返回该值。
对于尾递归,Scala语言特别增加了一个注释@tailrec,该注释可以确保程序员写出的程序是正确的尾递归程序,如果由于疏忽大意,写出的不是一个尾递归程序,则编译器会报告一个编译错误,提醒程序员修改自己的代码。

局限

由于JVM的限制,对尾递归深层次的优化比较困难,因此,Scala对尾递归的优化很有限,它只能优化形式上非常严格的尾递归。也就是说,下列情况不在优化之列。

如果尾递归不是直接调用,而是通过函数值。
比如以上阶乘的尾递归版本,如果我们改写为不是直接调用它,而是将函数赋值给func,编译器将不会认为它是尾递归。

//call function value will not be optimized
val func = factorialTailrec _
def factorialTailrec(n: BigInt, acc: BigInt): BigInt = {
     if(n <= 1) acc
     else func(n-1, acc*n)
}

间接递归不会被优化
间接递归,指不是直接调用自身,而是通过其他的函数最终调用自身的递归。如下所示。

//indirect recursion will not be optimized
def foo(n: Int) : Int = {
    if(n == 0) 0;
    bar(n)
}
def bar(n: Int) : Int = {
    foo(n-1)
}

参考资料

相关文章

  • scala尾递归

    什么是递归 一个函数直接或者间接的调用本身,这就是递归。典型案例斐波那契数列 以上的方法中当n>3时,就是典型的递...

  • 【Scala】尾递归优化

    以递归方式思考 递归通过灵巧的函数定义,告诉计算机做什么。在函数式编程中,随处可见递归思想的运用。下面给出几个递归...

  • Scala尾递归赋能业务

    业务背景 因为公司有多个公众号,并且是各不相同的商户主体,所以wechat_openid并不是相通的,在会员模块为...

  • Scala for循环与尾递归效率问题

    最近复习scala时突然想到的问题,就是尾递归和for循环的效率问题,闲来无聊就做了个测试, 先说结果,在5000...

  • Kotlin语言(九):特性

    1、尾递归优化 尾递归:函数在调用自己之后没有再执行其他任何操作就是尾递归 尾递归优化的原理就是将递归转换成迭代,...

  • 从示例逐渐理解Scala尾递归

    1.递归与尾递归 1.1 递归 1.1.1 递归定义 递归大家都不陌生,一个函数直接或间接的调用它自己本身,就是递...

  • 递归&尾递归

    调用栈的特点,先进后出, FILO, 场景还原。 递归 有栈溢出的可能 stack overflow 尾递归 编译...

  • 递归调用优化

    尾递归优化 函数调用自身,称为递归。如果尾调用自身,就称为尾递归。 递归非常耗费内存,因为需要同时保存成千上百个调...

  • 什么是尾调用?什么是尾递归?尾调用的优化?尾递归优化?

    尾调用优化 尾递归(尾调用优化)

  • C++ 递归算法

    递归算法,尾递归算法求阶乘!

网友评论

      本文标题:scala尾递归

      本文链接:https://www.haomeiwen.com/subject/lygomhtx.html