《javascript语言精粹》学习笔记 - 递归函数

递归函数就是会直接或者间接地调用自身的一种函数。递归是一种强大的编程技术,它把一问题分解为一组类似的子问题,每个都用一个寻常解去解决。通常来讲,一个递归函数调用自身去解决它的子问题。node

书上的一个小例子“汉诺塔”。塔上有3根柱子和一个套直径都不一样的空心圆盘。开始时源柱子的全部圆盘都按照从小到大的顺序堆叠。目标是每次移动一个圆盘到另外一根柱子,最终把一堆圆盘移动到目标柱子上,期间不容许把较大的圆盘放在较小的圆盘上。编程

var hanoi = function(disc, src, aux, dst){
    if (disc > 0) {
        hanoi(disc - 1, src, dst, aux);
        document.writeln('Move disc' + disc + ' form ' + src + ' to ' + dst);
        hanoi(disc - 1, aux, src, dst);
    }
};

hanoi(3, 'src', 'aux', 'dst');

运行代码结果:segmentfault

Move disc1 form src to dst
Move disc2 form src to aux
Move disc1 form dst to aux
Move disc3 form src to dst
Move disc1 form aux to src
Move disc2 form aux to dst
Move disc1 form src to dst
  • hanoi函数把一堆圆盘从一根柱子移动到另一根柱子,必要使用辅助的柱子。它把该问题拆分红3个小问题。它先移动到一对圆盘中较小的圆盘到辅助柱子上,从而露出下面较大的圆盘,而后移动下面的圆盘到目标柱子上。最后,他将刚才较小的圆盘从辅助柱子上再移动到目标柱子上。经过递推地调用自身去处理一堆圆盘的移动,从而解决那些问题。
  • 传递给hanoi函数的参数包括当前移动的圆盘的编号和它将要用到的3根柱子。当它调用自身的时候,它去处理当前正在处理的圆盘之上的圆盘。最终,他会一个不存在的圆盘编号去调用。在这样的状况下,他不执行任何操做。因为该函数对非法只不理会,也不用担忧死循环的问题。

书上第二个例子是说递归函数能够很是高效率的操做树形结构,好比DOM。每次递归调用是处理指定的树的一小段。数组

// 它从某个指定的节点开始,按照 HTML 源码中的顺序访问该数的每一个节点。
var walk_the_DOM = function(node, func){
    func(node);
    node = node.firstChild;
    while (node) {
        walk_the_DOM(node, func);
        node = node.nextSibling;
    }
};

// 它调用 walk_the_DOM 函数,传递一个用来查找节点属性名的函数为参数。
// 匹配的节点会累加到一个结果数组里面。
var getElementsByAttribute = function(att, val){
    var results = [];

    walk_the_DOM(document.body, function(node){
        var actual = node.nodeType === 1 && node.getAttribute(att);
        if (typeof actual === 'string' && (actual === val || typeof value 
            != 'string')) {
            results.push(node);
        }
    });

    return results;
};

有一些语言提供了尾递归的优化。这意味着若是一个函数返回自身递归的结果,那么调用的过程会被替换成以循环,能够提升速度。惋惜js并无尾递归的优化。函数

好运的是,ES6给咱们带来了尾递归,详细迎接ECMAScript 6, 使用尾递归
拓展:什么状况下用递归?优化

相关文章
相关标签/搜索