第六章 递归

作者: 549b968de8fa | 来源:发表于2017-03-26 14:56 被阅读50次

本章是一个过渡性章节,旨在平滑地从思考函数转向更深层次的函数式风格的思考。

6.1 自吸收函数(调用自己的函数)

从历史上看,递归和函数式编程有着不可不说的渊源,或者说,它们总是被一起介绍。但我们说,理解递归更有主语理解函数式编程,原因有三:

  1. 递归的解决方案包括使用对一个普通问题子集的单一抽象的使用。

  2. 递归可以隐藏可变状态

  3. 递归是一种实现懒惰和无限大结构的方法。

我们来看一个描述,并区分它们的不同之处:

假如你考虑一个函数,比如 myLength 接收一个数组,并返回它的长度,那么它的描述如下:

  1. 从零开始计算数组大小。
  1. 遍历数组,每遍历一个元素,数组大小(size)加 1。
  2. 遍历到数组结尾,那么数组的大小(size)就是它的长度(length)。

那么我们来看看递归的思路是什么样的:

  1. 如果数组是空的,那么应当返回 0。
  1. 对数组的剩余部分,添加一个结果到 myLength。

也就是:

function myLength(array) {

 if(_.isEmpty(array)) return 0;
 
 return 1 + myLength(_.rest(array))
}

我们这里不考虑那两个 lodash 中的方法,我们要理解它的思路是什么样的,当然,如果你说我们直接使用 array.length ,那我也无话可说了。
接着我们考虑这么一个情况,实现一个函数 zip,输出的结果为:

zip(['a', 'b', 'c'], [1, 2, 3]) ==> [ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ]

一般来说,看过 lodash
的源码后,都会写出这么一个函数:

function zip(array1, array2) {

 const length1 = array1.length;
 const length2 = array2.length;
 if(length1 !== length2) return;
​
 const result = [];
 let i = -1;
​
 while (++i < length1) {
   result[i] = [array1[i], array2[i]];
 }
​
 return result;
}

我们一般的思路就是采用循环来处理它,那么现在,我们要使用递归呢?

显然,递归不是用来操作这样的数的。

还记得阶乘吗?也就是 0! = 1,2! = 1 * 2,n! = 1 * 2 * 3 * ··· * n

递归是这样来用的:

function fact(n) {

 return n <= 0 ? 1 : n * fact(n - 1);
}
​
fact(5); // 120

注意到了吗?递归的使用场景是在当你可以调用函数自身在做输出的时候,才需要使用递归。

注意:在使用递归的时候,一定要注意设置停止条件,否则就会造成栈溢出。

这里再提到一个概念 —— 尾递归。

尾递归是指当对任何元素进行递归调用时,该函数的最后一个动作是递归调用,那么就称其为尾递归。

感兴趣的话,就自己查询下。

递归常常用于对象和数组中,这一点在 lodash 中得以体现。

可以看这里: Github

但是,我们更多将递归看作是底层操作。也就是说,在没有更好的办法时,应该减少递归的使用量,因为 JavaScript 引擎优化的问题,也是因为栈溢出的问题,也是因为它有时不是那么容易理解等问题。

总结

本节讲到了递归,也就是函数间接或直接的调用自己。

自递归调用是搜索以及处理嵌套数据结构的强大工具,但还是这句话,谨慎使用递归 ,因为递归有时候没有高阶函数来的那么直接。

我们目前普遍的认识还是使用函数组合,仅当需要的时候才使用递归这一技术操作。

相关文章

  • 第六章 递归

    本章是一个过渡性章节,旨在平滑地从思考函数转向更深层次的函数式风格的思考。 6.1 自吸收函数(调用自己的函数) ...

  • 二叉树遍历

    先序遍历——[递归、非递归] 中序遍历——[递归、非递归] 后序遍历——[递归、非递归] 层次遍历——[递归、非递归]

  • 二叉树的遍历

    先序递归: 非递归: 中序递归: 非递归: 后序递归: 非递归 层次遍历

  • 二叉树的前序、中序、后序遍历(递归、非递归)

    二叉树 前序 递归: 非递归: 中序 递归: 非递归: 层序 递归: 非递归:

  • 树的遍历,golang实现

    先序,递归 中序,递归 后序,递归 先序,非递归 中序,非递归 后序,非递归 层序遍历

  • 3 递归(19)(方法层面的高级循环)

    递归 树的递归 其它递归

  • 递归,递归,递归

    在我告诉你什么是递归之前,你应该读一下这篇文章:递归,递归,递归。 如果你没有这么做,那么表扬一下自己。如果你那么...

  • 数据结构-树的遍历

    1. 先序遍历 递归实现 非递归实现 2. 中序遍历 递归实现 非递归实现 3. 后序遍历 递归实现 非递归实现 ...

  • 树的遍历

    节点结构: 先序遍历 递归 非递归 后序遍历 递归 非递归 中序遍历 递归 非递归 层序遍历 类库 有了上述遍历算...

  • 算法图解系列之递归[03]

    3 递归 3.1 递归<函数> 3.2 基线条件和递归条件 3.3 递归调用栈

网友评论

    本文标题:第六章 递归

    本文链接:https://www.haomeiwen.com/subject/qtlkottx.html