摘要:介绍了尾调用/尾递归调用(tail call/tail recursive call)的一般概念、形式、用法,以及CPS(continuation-passing style)的编程模式。
函数式编程非常重要的一个概念是递归。在函数式编程里面,递归是原生的合乎情理的,是从lambda计算继承而来的;而迭代(循环)是不良的,因为它的副作用,因为迭代(循环)意味着修改变量的值,违背了函数式编程的本意。修改变量的值是某些纯粹的函数式编程语言所坚决摈弃的,比如haskell。而scheme采取了更务实的做法,支持修改变量的值,并且保留了迭代(循环)的语法。这是非常有趣和值得探讨的。
我们知道,当一个函数的返回语句是另一个函数的直接调用,可称为尾调用tail call。尾调用的优点是节省程序运行时间和空间。因为函数每一层的嵌套调用,都意味着一个新的栈帧stack frame。栈帧保留了诸如实参值,局部变量,访问链,返回地址等信息,构造和销毁栈帧是程序运行时的开销。而尾调用,可以避免在内存中保留caller函数的栈帧,只需要直接把caller的返回地址复制到callee的返回地址。一般情况下的尾调用,优势不明显,但是如果是层次非常多的递归调用,则可以大大发挥作用。这种递归叫做尾递归。优化后的尾递归,即直接传递返回地址的尾递归,与过程式编程的迭代(循环)类同。换句话说,也可以编译为goto或者jump语句。
以阶乘函数为例。
(define (factorial n)
(if (=