上一篇我们通过argument的callee初尝了10阶乘的递归结构(链接),这一次我们来深入了解一下js中包含的递归结构。
此次我们仍然用10!来讨论递归和循环的结构。首先我们回顾一下循环的结构:
//for循环求和
var sum=0;
for(var i=10; i>=1; i--){
sum=sum+i;
}
document.write(sum);
//do-while循环求和
var i = 10;
do{
sum=sum+i;
i--;
}while(i>=1)
//while循环求和
var i = 10;
while(i>=1){
sum=sum+i;
i--;
}
而递归结构:
function sum(i) {
if (i == 1) {
return 1; //递归最深层的1;
} else {
return i * sum( i - 1); //递归中的i*(i - 1)
}
}
console.log(sum(10));
解析递归与循环
循环和递归在执行时的逻辑结构是不同的。
循环的逻辑是分步执行:算完i=10,再算i=9;算完i=9再算i=8...
递归的逻辑是层级执行:算i=10需要先算出i=9;算i=9需要先算出i=8...一直到算出i=2需要先算出i=1,而i=1时在if中已经定义好了return的1,那么接下来只需要从i=1再得到i=2...一直返回到最外层的i=10.
递归的优势在于树状结构计算时,比线性分步结构的循环要计算量小很多:
试想树状结构,如果是循环结构,需要一条一条计算分支的结果,将会比从底层计算到顶的递归增加多少计算量。
堆栈模型解释递归
堆栈的数据结构模型可以很好地用于解释递归(也可以反过来说递归结构可以帮助理解堆栈的数据结构)。
堆(英语:Heap)是计算机科学中的一种树状数据结构,需要满足下层数据向高层数据之间存在递进关系。栈(stack)是另一种数据结构,它的特性在于LIFO(Last In - First Out),想象它像一个桶,在桶最上层的是最晚放进去的数据,但是却是最先被调取出来的。
以简单的堆栈模型来理解递归,好比当我们已知要计算i=n的值时,就需要将这一数据先放入桶里,然后依次递进,直到达到递归界限i=1(假设),i=1是最上层的值,但是最上层的值却是最易得到的。然后为了获得i=n,我们再依次将桶里的每一层i取出,直到取得i=n。
实现递归的argument.callee作用
当我们写一个函数递归,且不用callee时:
function loop(i){
var sum=0;
if (i>10) {
return sum;
}
sum = sum + i;
loop(i+1); //注意这里,在函数中引用函数自己实现递归。
}
loop (0);
在上面的代码中,函数名出现在了函数定义内形成了紧密耦合。当函数名出现变化,就必须修改从内到外的函数名称;或我们定义的是匿名函数的情况下,无法直接调用函数实现递归,这种情况下我们就需要使用callee。 但callee是个“昂贵”的操作,在每次迭代时会重新创建,将增加运行负担,需要酌情使用。
部分参考
计算机世界里的“堆栈”你真的懂吗? - 华清远见的文章 - 知乎 zhuanlan.zhihu.com/p/259807976
什么是堆? 什么是栈? - 白鲸鱼的文章 - 知乎 zhuanlan.zhihu.com/p/101531768