查看文章 |
尾递归(tail-recursive)
2009-03-17 20:56
尾递归(tail-recursive) 在进行函数调用时,如果函数体中的最后一个语句都是对系统中其他函数的调用,这个函数调用就是 尾递归的。欲说明尾递归,必须说明尾调用(tail-call)。例如下面的函数调用: 1 p() 2 { 3 ... 4 q(); 5 ... 6 } 7 8 q() 9 { 10 ... 11 r(); 12 s(); 13 } 在执行函数p的某个时候,函数q被调用。q中最后去调用s,当s返回的时候,将值返回给q,但是q原封 不动地将该值返回给p。这里q中最后对s的调用就称为尾调用。 在传统的栈机制计算机(stack machine)中,尾调用可以编译成直接跳转到s的代码处,而不用将返回 地址压入栈中,因为s返回的地方就是q要返回的地址。程序执行到这里的时候,栈中的返回地址本来就 是对的。故s的调用结束不需要返回到q中,而是直接返回到p函数体中对q调用的地方就可以了。 如果函数的所有可能执行路径都以尾调用结束,就说明该函数是尾递归的。 这一点是很重要的,尾递归函数可以在循环结构(loop)中被调用而不消耗栈空间。这种函数通常称为 “迭代函数”(iterative function)。 许多函数既可以用迭代风格来编写,也可以用非迭代(递归)风格来编写。例如求阶乘的函数,我们先 来写出它的非迭代递归式: factorial (0) -> 1; factorial (N) -> N * factorial(N-1). 在函数体factorial (N)分支中,最后是对一个表达式求值,而不是对一个函数的调用,所以他不是尾 调用,那么该函数自然就不是尾递归了。 现在写出它的迭代式,这里要引入一个辅助函数: factorial (N) -> factorial_1 (N,1). factorial_1 (0,X) -> X; factorial_1 (N,X) -> factorial_1 (N-1,N*X). 显然这是一个尾递归式。 许多非尾递归的函数可以通过引入一个辅助函数,增加一个额外参数(聚集器)的方法改写成尾递归的。 在函数式编程语言中,不单单是为了优化系统执行的目的,有时候函数必须要写成迭代式才能执行。例 如在无限循环结构中,直接递归,不单单是栈区会溢出,而且语义也难以满足。这也许是栈机制计算机 的不足之处。我们知道,在可计算理论中,可计算函数与递归函数是等价的,并且函数式编程在语义上 更加严谨。 |
最近读者: