SICP

递归与迭代
a linear recursive process
线性递归

  1. function factorial(n) {
  2. return n === 1
  3. ? 1
  4. : n * factorial(n - 1);
  5. }

21.05.23 - 图1
a linear iterative process
线性迭代

  1. function factorial(n) {
  2. return fact_iter(1, 1, n);
  3. }
  4. function fact_iter(product, counter, max_count) {
  5. return counter > max_count
  6. ? product
  7. : fact_iter(counter * product,
  8. counter + 1,
  9. max_count);
  10. }

21.05.23 - 图2
The expansion occurs as the process builds up a chain of deferred operations (in this case, a chain of multiplications).The contraction occurs as the operations are actually performed. This type of process, characterized by a chain of deferred operations, is called a recursive process.
递归的特性在于延迟操作,先展开收集(生成执行链),后执行(真正执行)收缩
the second process does not grow and shrink. At each step, all we need to keep track of, for any n_n, are the current values of the names product, counter, and max_count. We call this an _iterative process.
迭代始终真实执行,不做展开收缩,需要自行处理留存计算中间值。

In contrasting iteration and recursion, we must be careful not to confuse the notion of a recursive process with the notion of a recursive function.
递归过程不是递归函数。

When we describe a function as recursive, we are referring to the syntactic fact that the function declaration refers (either directly or indirectly) to the function itself. But when we describe a process as following a pattern that is, say, linearly recursive, we are speaking about how the process evolves, not about the syntax of how a function is written.
线性递归与线形迭代指的 是过程演化(执行图像),不是语法(实际代码)

It will execute an iterative process in constant space, even if the iterative process is described by a recursive function. An implementation with this property is called tail-recursive.[3] With a tail-recursive implementation, iteration can be expressed using the ordinary function call mechanism, so that special iteration constructs are useful only as syntactic sugar.
在常量空间中执行迭代过程,即使迭代过程是由递归函数描述的,这种实现叫 尾递归。
尾递归即是迭代,迭代即可用循环来实现。

尾递归其实是一种特殊的递归调用情况,全程无展开收缩的递归函数调用应该无需保留中间栈,一层函数执行完直接跳出,但是由于js没有尾递归优化,所以该耗的内存并不会减少(因为依旧是递归函数形式,编译器未在执行完一个函数后将栈弹出),所以将尾递归再转为循环,才能真正做到优化。
所有的尾递归都可转为循环。
https://zhuanlan.zhihu.com/p/36587160

  1. function fact(_n, _r) { // <= _n, _r 用作初始化变量
  2. var n = _n;
  3. var r = _r; // <= 将原来的 n, r 变量提出来编程迭代变量
  4. function _fact(_n, _r) { // <= 迭代函数非常简单,就是更新迭代变量而已
  5. n = _n;
  6. r = _r;
  7. }
  8. _fact_loop: while (true) { // <= 生成一个迭代循环
  9. if (n <= 0) {
  10. return r;
  11. } else {
  12. _fact(n - 1, r * n); continue _fact_loop; // <= 执行迭代函数,并且进入下一次迭代
  13. }
  14. }
  15. }

As a consequence, these languages can describe iterative processes only by resorting to special-purpose looping constructs such as do, repeat, until, for, and while.
在没有尾递归的语言中,只可通过一些循环构造来实现迭代