Skip to content

Commit c0b8cc2

Browse files
committed
Adds an explanation of what tail call recursion with an example; shows how recursive definitions need to differ to take advantage of TCO
1 parent 47cbdc7 commit c0b8cc2

1 file changed

Lines changed: 50 additions & 0 deletions

File tree

tailCalls/tailCalls.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,54 @@
22

33
For those who have been proponents of functional JavaScript the arrival of 'proper tail calls' is the most exciting feature of Es6.
44

5+
Tail call optimisation (TCO) is a way to make recursively-defined functions more space efficient; that is functions that call themselves multiple times with different arguments are less likely to use up all your memory.
6+
7+
Let's look at a function defined with loops and a function defined recursively and see the difference in memory usage.
8+
9+
```js
10+
function loopyFactorial (n) {
11+
var result = 1;
12+
for (var i = 1; i <= n; i++){
13+
result *= i;
14+
}
15+
return result;
16+
}
17+
18+
function recursiveFactorial (n) {
19+
if (n < 2) { return 1; }
20+
else { return n * recursiveFactorial(n-1); }
21+
}
22+
```
23+
24+
Let's see what happens in each case when I call the function on 3. In the case of `loopyFactorial`,
25+
1. I start off by setting `result` and `i` to one.
26+
2. Then, I check if `i` is less than or equal to 3.
27+
3. It is, so I multiply 3 by 1 and add 1 to `i`.
28+
4. I go through steps 2 and 3 again 2 more times. After that, 2 `i` is greater than 3, so I return the value of result.
29+
30+
At every point, all I have to remember are the values of `i` and `result`. Now let's look at `recursiveFactorial`.
31+
1. I check whether 3 is less than 2.
32+
2. It isn't, so I return 3 multiplied by `recursiveFactorial(2)`. Before I can do that, I have to work out what `recursiveFactorial(2) *is*.
33+
1. I check whether 2 is less than 2.
34+
2. It isn't, so I return 2 multiplied by `recursiveFactorial(1)`. Before I can do that, I have to work out what `recursiveFactorial(1) *is*.
35+
1. I check whether 1 is less than 2.
36+
2. It is! I return 1.
37+
3. I return 2 * 1 = 2
38+
3. I return 3 * 2 = 6
39+
40+
The problem with the recursive function is the bulging out of the lists within lists -- I am having to remember where to go back to after I finish each stage! Recursively-defined functions don't have to bulge this way.
41+
42+
```js
43+
function slimRecursor (n, m) {
44+
if (n < 2) { return m; }
45+
else { return slimRecursor (n - 1, m * n); }
46+
}
47+
48+
function slimRecursiveFactorial (n) {
49+
return slimRecursor (n, 0);
50+
}
51+
```
52+
53+
How much memory is `slimRecursiveFactorial` going to use? A similar amount to `loopyFactorial` or a similar amount to `recursiveFactorial`? The answer depends on whether we have TCO or not! TCO lets us collapse a chain of `return`s into one `return`. That is, if a function returns another call to itself *without having to do any other computation on the result of that call*, we can only remember where we want the final computation to end up, and not all the places it needs to stop along the way.
54+
555
Es6 now has built in [TCO](http://duartes.org/gustavo/blog/post/tail-calls-optimization-es6/) (tail call optimization) which means that we can replace loops with recursive functions and write JavaScript in a more functional style.

0 commit comments

Comments
 (0)