函数式编程的一个强大之处在于递归,使用递归可以简化算法设计的思路。
尾递归是递归的一种特殊形式,它的特点是可以不创建新的堆栈帧而是改变当前堆栈帧来实现,这样做的好处是不会浪费运行时空间,递归层次加深也不会发生栈溢出,如同执行迭代一样。
如何将非尾递归函数改写成尾递归函数是函数式编程的一项重要的基本功,而最简单的题目就是“n的阶乘”。有许多blog讲解如何将“n的阶乘”改写成尾递归的形式,而这里假设读者已经具备写出尾递归版本的“n的阶乘”的能力。
今天我们来看另外一个经典的题目: 链表插入元素。
题目的描述是这样的:给定一个有序链表L和一个元素v,将v插入到L中并保持新链表仍然有序。例如 L = List(1, 2, 4, 5), v = 3,函数应该返回List(1, 2, 3, 4, 5)。
递归的思路:如果当前链表为空,直接返回只有一个元素v的链表。否则需要比较链表头部x和v的大小,如果x < v,那么将链表尾部作为L继续调用当前函数,然后将x和结果连接起来;如果 x >= v,那么将v和L连接起来返回。
这样我们就很容易可以写出简单递归第一个版本:
def insert1(l: List[Int], v: Int): List[Int] = l match {
case List() => List(v)
case x :: xs =>
if (x < v) x :: insert1(xs, v)
else v :: l
}
这个版本很简单也很直观,但是很可惜它不是尾递归的。当x < v的时候,递归调用之后又执行了一次cons&