通过阶乘计算来认识尾递归。阶乘可以用下面的表达式来描述:
n!=n*(n-1)*(n-2)…3*2*1
根据上面的表达式我们可以概括出下面的算法来计算阶乘:
n!=n*(n-1)!
public int Factorial(int number) { if (number == 1) { return 1; } var temp = number * Factorial(number - 1); return temp; }
函数调用:
var calculator=new Calculator(); var number = calculator.Factorial(6);
下面的替换模型描述了计算机是如何执行这一代码的:
当我们使用一个过大的数值,例如求:Factorial(5000)将会发生StackOverFlowException。
为了将它转化为一个尾递归函数,可以使用一种提供“累加器参数”的技术,我们需要向函数中添加一些参数,用来提供当前结果。
提供product参数,product=product*currentCount
提供currentCount参数,currentCount=currentCount+1
下面的代码描述了改造后的代码:
public int Factorial(int number) { return TailedFactorial(1,1, number); } public int TailedFactorial(int product, int currentNumber, int number) { if (currentNumber > number) { return product; } var temp = TailedFactorial(product*currentNumber, ++currentNumber, number); return temp; }
与前面一样,我们看看这段代码的替换模型:
考虑第一个计算过程,这一过程展示了一个先逐步展开而后收缩 的形状,在展开阶段描述了计算机在每次调用时如何向堆栈中添加一个堆栈帧的,收缩阶段则表现为这些运算的实际执行。要执行这种计算过程,计算机就需要维护好那些以后将要执行的操作轨迹。
与之对应,第二个计算过程并没有任何增长或收缩。对于任何一个n,在计算过程中的每一步,所有的东西都保存在变量product,currentNumber和number中,在这种场景下,在计算过程中的任何一点,这几个变量都提供了有关计算状态的一个完整描述。入股我们令上述计算在某两个步骤之间停下来,要想重新唤醒这一计算,只需要提供这三个变量即可。我们称这样的特性为尾递归。
参考书籍:SICP
原文:http://www.cnblogs.com/richieyang/p/5410464.html