为什么尾递归能优化


要知道一个道理,计算机特别讨厌递归程序,特别讨厌。因为处理不好,内存就被吃光了。

递归

百度百科

程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

构成递归需具备的条件:

  1. 子问题须与原始问题为同样的事,且更为简单;
  2. 不能无限制地调用本身,须有个出口,化简为非递归状况处理。

所以综上所述,函数自己调用自己就是递归了么?不是,函数自己调用自己只是递归的一个先决条件,还有一个条件是,需要有函数的终止条件。否则递归就是一个死循环。

尾递归

百度百科

如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾递归函数的特点是在回归过程中不用做任何操作,这个特性很重要,因为大多数现代的编译器会利用这种特点自动生成优化的代码。

我们可以这样理解尾递归

  1. 所有递归形式的调用都出现在函数的末尾
  2. 递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分

递归是咋调用的呢

递归就是自身调用自身,而自身是一个函数,一个函数在执行的时候一定要吧这个函数压入栈中,执行的时候在出栈。所以一个函数在执行的时候先把遇到的函数压入栈中,那入栈的时候就要申请空间嘛,这个空间可以裂解为就是存放函数的一个空间,如果递归的层次特别多,那么就要申请特别多的占空间来存放一个函数的各种参数,久而久之就OOM了

为什么尾递归能优化

当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活动记录而不是在栈中去创建一个新的。编译器可以做到这点,因为递归调用是当前活跃期内最后一条待执行的语句,于是当这个调用返回时栈帧中并没有其他事情可做,因此也就没有保存栈帧的必要了。通过覆盖当前的栈帧而不是在其之上重新添加一个,这样所使用的栈空间就大大缩减了,这使得实际的运行效率会变得更高。

尾递归优化了什么

通过上面的描述,我们知道,尾递归和普通递归的区别就在一,它就覆盖当前的活动记录而不是在栈中去创建一个新的 这句话怎么理解呢。就是尾递归只是复用了当前的函数栈而不是重新压入一个栈。所以尾递归的优化点就在这儿。

为什么普通递归就不能优化

为什么普通递归函数无法像尾递归函数一样优化呢?明明都是递归函数,怎们普通递归就不能再编译阶段优化成循环的形式呢?

就想上面说的当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活动记录而不是在栈中去创建一个新的。普通递归调用则实现不了,因为普通递归调用必须等待上一个函数的完整执行和返回,因此上一个函数必须入栈才行。

Java Vs Scala 尾递归优化

Java 没有实现尾递归优化,Scala有实现

  • 未完待续

评论