论坛首页 综合技术版 FP

函数式编程疑问

浏览 298 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
时间:2008-08-04
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
	<script language="JavaScript" type="text/JavaScript">
	function oldRecursion(n){
		var j=1;
		for(var i=n;i>0;i--){
			j*=i;
		}
		return j;
	}
	function recursion(n){
		return n==1?1:n*recursion(n-1);
	}
	function tailRecursion(n, a){
	    a = a||1;
	    return n==1?a:tailRecursion(n-1, a*n);
	}
	
	
//	-----------------------------------------------------------------------------------------
	var start=new Date();
	for(var i=0;i<100000;i++){
		r1=oldRecursion(3);
	}
	var end=new Date();
	document.write(""+(end-start)+"<br/>");
//	-----------------------------------------------------------------------------------------
	var start=new Date();
	for(var i=0;i<100000;i++){
		r2=recursion(3);
	}
	var end=new Date();
	document.write(""+(end-start)+"<br/>");
//	-----------------------------------------------------------------------------------------
	var start=new Date();
	for(var i=0;i<100000;i++){
		r3=tailRecursion(3);
	}
	var end=new Date();
	document.write(""+(end-start)+"<br/>");
	document.write(r1+" "+r2+" "+r3);
//	-----------------------------------------------------------------------------------------
	</script>
</html>


我在jstang看到过一个帖子“阶乘(factorial)&尾递归(Tail Recursion)http://jstang.5d6d.com/thread-1750-1-1.html
里面讲“效率上和循环迭代、阶乘改进算法相当甚至稍胜出(ie6,firefox2,safari3),普通递归的效率最为底下,且需要深入堆栈”
但是测试表明 尾递归效率最低啊
结果是:
  • 循环:516
  • 递归:1000
  • 尾递归:1046

js语言精髓与编程实践里面也说啦“然而不幸的是。目前已知的javascript 的解释环境中并不支持这种特性(尾递归)。因此,我们在这里讨论函数室式时,可以说“能够通过函数递归来消灭循环语句”,但在不支持尾递归(及其优化技术)的javascript中,这种实现将以大量栈和内存消耗为代价”

那到底尾递归该如何理解呢?
我的测试正确的吗?
   
时间:2008-08-07
换一个支持尾递归的测试
   
0 请登录后投票
时间:2008-08-07
我已经不是第一次看到自称程序员连尾递归都不知道的人了
真的,很好奇
这些人们上大学的时候都干嘛呢?
怎么学会编程的?必定有些什么我等资质愚钝者所不解的秘诀吧?
   
0 请登录后投票
时间:2008-08-07
引用
尾递归是针对传统的递归算法而言的, 传统的递归算法在很多时候被视为洪水猛兽. 它的名声狼籍, 好像永远和低效联系在一起.
尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量. 直接让被调用的函数返回时越过调用者, 返回到调用者的调用者去.

以下是具体实例:

线性递归:
long Rescuvie(long n) {      
  return(n == 1) ? 1 : n * Rescuvie(n - 1);      
} 


尾递归:
long TailRescuvie(long n, long a) {      
  return(n == 1) ? a : TailRescuvie(n - 1, a * n);        
}      

long TailRescuvie(long n) {//封装用的      
  return(n == 0) ? 1 : TailRescuvie(n, 1);   
}  


当n = 5时
对于线性递归, 他的递归过程如下:
Rescuvie(5)  
{5 * Rescuvie(4)}
{5 * {4 * Rescuvie(3)}}
{5 * {4 * {3 * Rescuvie(2)}}}
{5 * {4 * {3 * {2 * Rescuvie(1)}}}}
{5 * {4 * {3 * {2 * 1}}}}
{5 * {4 * {3 * 2}}}
{5 * {4 * 6}}
{5 * 24}
120


对于尾递归, 他的递归过程如下:
TailRescuvie(5)
TailRescuvie(5, 1)
TailRescuvie(4, 5)
TailRescuvie(3, 20)
TailRescuvie(2, 60)
TailRescuvie(1, 120)
120


很容易看出, 普通的线性递归比尾递归更加消耗资源, 在实现上说, 每次重复的过程
调用都使得调用链条不断加长. 系统不得不使用栈进行数据保存和恢复.而尾递归就
不存在这样的问题, 因为他的状态完全由n和a保存.
   
0 请登录后投票
论坛首页 综合技术版 FP

跳转论坛:
JavaEye推荐