zoukankan      html  css  js  c++  java
  • javascript递归

    递归函数就是会直接或者间接调用自身的一种函数。递归是一种强大的编程技术,它把一个问题分解为一组相似的子问题,调用自身去解决它的子问题。

    一、汉诺塔

    问题描述:有3根柱子和一套直径各不相同的空心圆盘。开始时源柱子上的所有圆盘都按照从小到大的顺序堆叠。目标是通过每次移动一个圆盘到另一根柱子,最终把一堆圆盘移动到目标柱子上,过程中不允许把较大的圆盘放置在较小的圆盘上。

    var hanoi=function(disc,src,aux,dst){
        if(disc>0){
            hanoi(disc-1,src,dst,aux);
            console.log("move "+disc+" from "+src+" to "+dst);
            hanoi(disc-1,aux,src,dst);
        }
    }

    圆盘数量为3的时候解法为:

    hanoi函数把一堆圆盘从一根柱子移动到另一根柱子,必要时使用辅助的柱子。

    它把问题分解成3个子问题:
    首先,移动一对圆盘中较小的圆盘到辅助柱子上,从而露出下面较大的圆盘。

    然后,移动下面较大的圆盘到目标柱子上。

    最后,它将刚才较小的圆盘从辅助的柱子上再移动到目标柱子上。

    传递给hanoi函数的参数包括当前移动的圆盘编号和它将用到的3根柱子。当它调用自身的时候,它去处理当前正在处理的圆盘之上的圆盘。最终,它会以一个不存在的圆盘编号去调用。此时,不执行任何操作。由于该函数对非法值不予理会,就不用担心它会导致死循环。

    二、DOM遍历

    递归函数可以非常高效地操作树形结构,比如浏览器文档对象模型DOM。每次递归调用处理指定树的一小段。

    html结构如下:

    <body>
    <div class="test">测试div</div>
    <span class="test">测试span</span>
    <div class="test1">test1 div</div>
    </body>

    js如下:

    <script>
    /*定义walk_the_DOM函数,它从某个指定的节点开始,按HTML源码中的顺序访问该树的每个节点。
    它会调用一个函数,并依次传递每个节点给它。walk_the_DOM调用自身去处理每一个子节点。
    */
    var walk_the_DOM=function walk(node,func){
        func(node);
        node=node.firstChild;
        while(node){
            walk(node,func);
            node=node.nextSibling;
        }
    }
    
    /*定义getElementsByAttribute函数。它以一个属性名称字符串和一个可选的匹配值作为参数。
    它调用walk_the_DOM,传递一个用来查找节点属性名的函数作为参数。
    匹配的节点会累加到一个结果数组中。
    */
    var getElementsByAttribute=function(att,value){
        var results=[];
    
        walk_the_DOM(document.body,function(node){
            var actual=node.nodeType===1&&node.getAttribute(att);
            if(typeof actual==='string' &&( actual===value|| typeof value!=='string')){
                results.push(node);
            }
        });
        return results;
    }
    console.log(getElementsByAttribute("class","test"));//[div.test, span.test]

    三、命名函数表达式和递归

    1、递归问题

    求阶乘的函数:

    function factorial(num){
        if(num<=1){
            return 1;
        }else{
            return num*factorial(num-1);
        }
    }

    正常情况运行没问题,但是下面操作会让它出错:

    var anotherFactorial=factorial;//把函数保存在遍历anotherFactorial中
    factorial=null;//factorial置为null,此时指向原始函数的引用只剩一个
    anotherFactorial(3)

    factorial已经不再是函数,所以会报错。

    2、arguments.callee实现递归

    arguments.callee是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用

    //console
    function factorial(num){
        if(num<=1){
            return 1;
        }else{
            return num*arguments.callee(num-1);
        }
    }
    var anotherFactorial=factorial;
    factorial=null;
    anotherFactorial(3) //6

    用arguments.callee代替函数名,可以确保无论怎样调用函数都不会出问题。因此,在编写递归函数时,使用arguments.callee总比使用函数名更保险。

    问题:在严格模式下,不能通过脚本访问arguments.callee,访问这个属性会报错。

    更多严格模式相关内容可参考:javascript 语句和严格模式(三)

    3、命名函数表达式实现递归

     创建一个名为f()的命名函数表达式,然后赋值给factorial,即使把函数赋值给了另一个变量,函数的名字f仍然有效,所以递归调用照样能正常完成。

    这种方式在严格模式和非严格模式都可行。

    //console
    var factorial =function f(num){
    'use strict'
        if(num<=1){
            return 1;
        }else{
            return num*f(num-1);
        }
    }
    
    factorial(3)
    //6
    var anotherFactorial=factorial;
    factorial=null;
    anotherFactorial(3)
    //6

    命名函数表达式也有它的一些经典bug,见javascript 函数和作用域(函数,this)(六)

    本文作者starof,因知识本身在变化,作者也在不断学习成长,文章内容也不定时更新,为避免误导读者,方便追根溯源,请诸位转载注明出处:http://www.cnblogs.com/starof/p/6510723.html有问题欢迎与我讨论,共同进步。

  • 相关阅读:
    数据库索引的作用和优势缺点
    Python 新浪微博元素 (Word, Screen Name)词汇多样性
    解决Myeclipse在port占用,导致tomcat无法启动。(Linux)
    linux命名管道通信过程
    Lua环境搭建之使用EditPlus搭建Lua开发环境
    详解LUA开发工具及其环境配置
    UltraEdit配置python和lua环境
    Lua学习笔记
    Linux 安装ibus极点五笔输入法备忘录
    win2k/xp查看当前进程
  • 原文地址:https://www.cnblogs.com/starof/p/6510723.html
Copyright © 2011-2022 走看看