Skip to content

Commit f090342

Browse files
recursive
1 parent f1a2ae5 commit f090342

File tree

2 files changed

+208
-0
lines changed

2 files changed

+208
-0
lines changed

Note/recursive/recursive.js

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
'use strict';
2+
3+
(function(){
4+
5+
6+
function factorial(n) {
7+
if (n==1) return 1;
8+
return n * factorial(n-1);
9+
}
10+
11+
// 这是一个尾调用
12+
function a(a){return a+1};
13+
function b(x) {
14+
return a(x);
15+
}
16+
function c(x) {
17+
if (x>0) {
18+
return a(x);
19+
}
20+
return b(x);
21+
}
22+
// 以下不属于尾调用
23+
function d(x) {
24+
return a(x)+1;
25+
}
26+
function e(x) {
27+
const res = a(x);
28+
return res;
29+
}
30+
31+
// 尾递归
32+
function factorial(n, total=1) {
33+
if (n===1) return total;
34+
return factorial(n-1, n*total);
35+
}
36+
37+
38+
}); //();
39+
40+
41+
42+
(function(){
43+
44+
function fibonacci(n) {
45+
if (n===0) return 0;
46+
if (n===1) return 1;
47+
// 此处非尾调用, 需在当前函数内递归执行完两个函数, 相加后再返回销毁当前函数
48+
// 当递归较深时, 容易堆栈溢出
49+
return fibonacci(n-1) + fibonacci(n-2);
50+
}
51+
// 此时导致堆栈溢出
52+
// console.log(fibonacci(10000));
53+
// RangeError: Maximum call stack size exceeded
54+
55+
// 如改成尾递归
56+
'use strict';
57+
function fibonacciTail(n, cur=0, next=1) {
58+
59+
if (n===0) return cur;
60+
// n 作为计数器, 用来记录叠加几次
61+
// cur 作为当前次数, 对应的值
62+
// next 则是当前次数对应的下次的值
63+
// 此处属于尾调用, 但浏览器未必会进行尾递归优化
64+
return fibonacciTail(n-1, next, cur+next);
65+
}
66+
// 默认不优化,此处也会导致堆栈溢出
67+
console.log(fibonacciTail(10000));
68+
69+
}); //();
70+
71+
(function() {
72+
// 蹦床函数
73+
function trampoline(fn) {
74+
while(fn && typeof fn === 'function') {
75+
fn = fn();
76+
}
77+
return fn;
78+
}
79+
80+
// 改写递归函数
81+
function fibonacci(n, a=0, b=1) {
82+
if (n<=0) return b;
83+
[a, b] = [b, a+b];
84+
return fibonacci.bind(null, a, b);
85+
}
86+
console.log(trampoline(fibonacci(10000)));
87+
}); //();
88+
89+
(function(){
90+
91+
function fibonacci(n) {
92+
let cur=0;
93+
let next=1;
94+
while(n) {
95+
[cur, next] = [next, cur+next];
96+
n--;
97+
}
98+
return cur;
99+
}
100+
console.log(fibonacci(10000));
101+
102+
})();
103+
104+
105+
106+
107+

Note/recursive/recursive.md

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
## 递归
2+
3+
### 基本概念
4+
* 递归定义:在函数内,直接或间接调用自己,且存在终止条件,此函数称为递归函数
5+
* 如:
6+
7+
```js
8+
// 这就是一个递归函数
9+
function factorial(n) {
10+
if (n==1) return 1;
11+
return n * factorial(n-1);
12+
}
13+
```
14+
* 尾调用:在函数最后一步,调用函数,称为尾调用
15+
* 如:
16+
17+
```js
18+
// 这是一个尾调用
19+
function a(a){return a+1};
20+
function b(x) {
21+
return a(x);
22+
}
23+
function c(x) {
24+
if (x>0) {
25+
return a(x);
26+
}
27+
return b(x);
28+
}
29+
// 以下不属于尾调用
30+
function d(x) {
31+
return a(x)+1;
32+
}
33+
function e(x) {
34+
const res = a(x);
35+
return res;
36+
}
37+
```
38+
39+
* 尾递归:如果尾调用调用自身,就称为尾递归
40+
* 如:
41+
42+
```js
43+
// 尾递归
44+
function factorial(n, total=1) {
45+
if (n===1) return total;
46+
return factorial(n-1, n*total);
47+
}
48+
```
49+
50+
### 递归的优缺点
51+
* 优点
52+
* 代码简洁,可读性高
53+
* 大问题分解为小问题,解决思路简单
54+
* 缺点
55+
* 创造大量的执行栈,较深的递归可能造成堆栈溢出
56+
* 多层调用,浪费空间和时间
57+
58+
### 递归的优化
59+
* 递归函数改写尾递归
60+
* 注意:尾递归有隐式优化和调用栈丢失的问题,通常默认不开启
61+
* 递归改写迭代/循环
62+
63+
```js
64+
function fibonacci(n) {
65+
let cur=0;
66+
let next=1;
67+
while(n) {
68+
[cur, next] = [next, cur+next];
69+
n--;
70+
}
71+
return cur;
72+
}
73+
console.log(fibonacci(10000));
74+
```
75+
76+
* 蹦床函数
77+
78+
```js
79+
// 蹦床函数
80+
function trampoline(fn) {
81+
while(fn && typeof fn === 'function') {
82+
fn = fn();
83+
}
84+
return fn;
85+
}
86+
87+
// 改写递归函数
88+
function fibonacci(n, a=0, b=1) {
89+
if (n<=0) return b;
90+
[a, b] = [b, a+b];
91+
return fibonacci.bind(null, a, b);
92+
}
93+
console.log(trampoline(fibonacci(10000)));
94+
```
95+
96+
97+
### 参考资料
98+
* [ECMAScript 6 入门 -- 函数的扩展](http://es6.ruanyifeng.com/#docs/function#%E5%B0%BE%E8%B0%83%E7%94%A8%E4%BC%98%E5%8C%96)
99+
* [JavaScript专题之递归](https://juejin.im/post/59b88ede5188256c60692a85#heading-6)
100+
* [尾调用优化](http://www.ruanyifeng.com/blog/2015/04/tail-call.html)
101+

0 commit comments

Comments
 (0)