前言

最近在读《JavaScript语言精粹》,对递归函数有了进一步的认识,希望总结下来:

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

"汉诺塔"经典递归问题

"汉诺塔"是印度的一个古老传说,也是程序设计中的经典的递归问题,是一个著名的益智游戏:

  题目如下:

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

    

寻找规律(把所有的圆盘移动到C):

  1)n(圆盘个数) == 1

    第一次:1号盘  A -> C      sum(移动次数) = 1

  2)n == 2

    第一次:1号盘 A -> B

    第二次:2号盘 A -> C

    第三次:1号盘 B -> C  sum = 3

  3)n == 3

    第一次:1号盘 A -> C

    第二次:2号盘 A -> B

    第三次:1号盘 C -> B

    第四次:3号盘 A -> C

    第五次:1号盘 B -> A

    第六次:2号盘 B -> C

    第七次:1号盘 A -> C  sum = 7

  以此类推...

  故不难发现规律,移动次数为:sum = 2^n - 1 

算法分析(递归):

  把一堆圆盘从一个柱子移动另一根柱子,必要时使用辅助的柱子。可以把它分为三个子问题:

    首先,移动一对圆盘中较小的圆盘到辅助柱子上,从而露出下面较大的圆盘,

    其次,移动下面的圆盘到目标柱子上

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

   把三个步骤转化为简单数学问题:

    (1)     把 n-1个盘子由A 移到 B;

    (2)     把 第 n个盘子由 A移到 C;

    (3)     把n-1个盘子由B 移到 C;

  我们创建一个JS函数,当它调用自身的时候,它去处理当前正在处理圆盘之上的圆盘。最后它回一个不存在圆盘去调用,在这种情况下,它不在执行任何操作。

JavaScript源代码实现

var hanoi = function(disc,src,aux,dst){
if(disc>0){
hanoi(disc-1,src,dst,aux);
console.log(' 移动 '+ disc + ' 号圆盘 ' + ' 从 ' + src + ' 移动到 ' + dst);
hanoi(disc-1,aux,src,dst)
}
} hanoi(3,'A','B','C')

整个算法的思路是:

  1. 将A柱子上的n-1个盘子暂时移到B柱子上
  2. A柱子只剩下最大的盘子,把它移到目标柱子C上
  3. 最后再将B柱子上的n-1个盘子移到目标柱子C上

JS递归函数遍历Dom

  递归函数可以非常高效的操作树形结构,在JavaScript有一种"天然的树形结构"浏览器端的文档对象模型(Dom)。每次递归调用时处理指定树的一小段。

/*      我们定义一个walk_the_DOM函数,
1) 它从某个指定的节点开始,按指定HTML源码的顺序,访问树的每个节点
2)它会调用一个函数,并依次传递每个节点给它,walk_the_DOM调用自身去处理每一个节点
*/
var walk_the_DOM = function walk( node , func ) {
func(node);
node = node.firstChild;
while (node) {
walk( node , func );
node = node.nextSibling;
}
} /* 在定义一个getElementByAttribute函数
1) 它以一个属性名称字符串和一个可选的匹配值作为参数
2) 它调用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;
}

命名函数表达式和递归

递归问题

求阶乘的函数:

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

通过将函数factorial设置为null,使原始函数的引用只剩一个, 此时factorial已不再是函数

arguments.callee实现递归

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

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

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

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

命名函数表达式实现递归

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

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

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

写在后面

何不在别人去"坚持"的时间,试着让自己去爱...因为喜爱,所以我们付出,但正是因为付出了,所以我们只能更爱.

大学生一枚才疏学浅,如有纰漏,还望前辈指正。

JS函数-我调用自己试试看的更多相关文章

  1. js函数的调用问题

    1.js函数的调用方式有三种.请问以下“二”处的几行代码有什么猫腻? //一 事件调用 btn.onclick=fn; //二 直接调用(window调用) fn(); //自上而下解析到这一行的时候 ...

  2. flash、js 函数 互相调用

    js调用flex函数 flex 利用ExternalInterface.addCallback(“注册的方法名”,As中的函数名)进行注册 js中,用document.getElementById(“ ...

  3. js 函数的调用模式

    1.函数调用 调用一个函数将暂停当前函数的执行,传递控制权和参数给新函数.除了函数声明时定义的形参,每个函数还接受两个附加的参数:this和arguments(arguments并不是一个真正的数组, ...

  4. js函数简单调用

    <script> //最简单的调用 //这是JavaScript DOM编程艺术(第2版)关于函数的原码 function convertToCelsius(temp) { var res ...

  5. VC与JavaScript交互(二) --- 调用JS函数

    这一章,我们来动手实践VC调用JS函数. 我们动手写一个HTML,其中包含这样一段JS代码: //[html] <script type="text/javascript"& ...

  6. js函数、表单验证

    惊天bug!!!在script里面只要有一点点错误,就都不执行了!!!所以每写一个方法,就跑一下,因为这个书写疏忽导致的bug不可估量!!! [笑哭,所以我才这么讨厌js么,后来真心的是一点都不想再看 ...

  7. Js函数的概念、作用、创建、调用!

    一.函数是用来帮助我们封装.调用代码的最方便的工具! 二.函数的创建方法有三种: 三.函数的创建方式有3种,调用方式也不是单一的,调用方式有4种!      1.作为一个函数去调用 函数名+();(函 ...

  8. JS中函数的调用和this的值

    调用每一个函数会暂停当前函数的执行,传递控制权和参数给新函数.除了声明时定义的形式参数,每个函数还接收两个附加的参数:this 和 arguments. 参数this在面向对象编程中非常重要,他的值取 ...

  9. AS与JS相互通信(Flex中调用js函数)

    转载自http://www.blogjava.net/Alpha/archive/2009/06/27/284373.html Flex中As调用Js的方法是:     1.导入包 (import f ...

随机推荐

  1. CentOS-7.2添加桌面快捷方式

    一,在桌面新建一个文件 文件名随意,但必须带有.desktop的后缀名. gedit /home/zgw/Desktop/zgw.desktop 二,在文件中写入如下内容 [Desktop Entry ...

  2. FZU 2253 salty fish

    https://vjudge.net/problem/FZU-2253 题意:略 思路: 一开始改变区间,还以为是线段树...还是dp的题做得太少了. 这题一开始我们可以统计出一共有多少只翻身的咸鱼, ...

  3. RabbitMq学习一入门篇(hello world)

    简介 RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python.Ruby..NET.Java,也是众多消息队列中表现不俗的一员,作用就是提高系统的并发 ...

  4. VSCode自定义配色方案

    说明 本文更新于2017-07-24,使用VSCode 1.14.1,操作系统为Windows. 配置文件 "文件-首选项-颜色主题"即可显示所有可用的颜色主题,上下选择后Ente ...

  5. KMP算法 --- 在文本中寻找目标字符串

    KMP算法 --- 在文本中寻找目标字符串 很多时候,为了在大文本中寻找到自己需要的内容,往往需要搜索关键字.这其中就牵涉到字符串匹配的算法,通过接受文本和关键词参数来返回关键词在文本出现的位置.一般 ...

  6. 聊聊synchronized的锁问题

    本文不会特别深入细致地讨论synchronized,只是做一个通俗易懂的说明,以及看清一类问题的本质,希望能给大家一点启发. 问题描述 有一个类A,提供了三个方法.分别是静态同步方法,非静态同步方法, ...

  7. 表达式求值(栈方法/C++语言描述)(一)

    一个算数表达式(以下简称为表达式)由运算数.运算符.左括号和右括号组成,定义一个枚举类型TokenType表示为: typedef enum { BEGIN, NUMBER, OPERATOR, LE ...

  8. Zabbix监控nginx性能

    编辑nginx的配置文件nging #配置ngx_status location /nginx_status{ stub_status on; access_log off; } #重启nginx # ...

  9. [BZOJ3038]上帝造题的七分钟2 树状数组+并查集

    考试的时候用了两个树状数组去优化,暴力修改,树状数组维护修改后区间差值还有最终求和,最后骗了40分.. 这道题有好多种做法,求和好说,最主要的是开方.这道题过的关键就是掌握一点:在数据范围内,最多开方 ...

  10. python实战===输入密码以******的形式在cmd中展示

    #设置密码输入,显示为****** import msvcrt,sys def pwd_input(): chars = [] while True: try: newChar = msvcrt.ge ...