概述

这是我在学习函数式编程的时候,关于递归尾递归相互递归蹦床函数的一些心得,记下来供以后开发时参考,相信对其他人也有用。

参考资料:JavaScript玩转Clojure大法之 - Trampoline

递归

我们知道,es5是没有尾递归优化的,所以在递归的时候,如果层数太多,就会报“Maximum call stack size exceeded”的错误。就连下面这个及其简单的递归函数都会报“Maximum call stack size exceeded”的错误。

function haha(a) {
if(!a) return a;
return haha(a-1);
} haha(100); //输出0
haha(12345678); //输出“Maximum call stack size exceeded”

为什么会报“Maximum call stack size exceeded”的错误?我觉得原因是在每次递归调用的时候,会把当前作用域里面的基本类型的值推进栈中,所以一旦递归层数过多,栈就会溢出,所以会报错。

注意:

  1. js中的栈只会储存基本类型的值,比如:number, string, undefined, null, boolean。
  2. 为什么在调用下一层递归函数的时候没有释放上一层递归函数的作用域?因为在回来的时候还需要用到里面的变量。

尾递归

怎么优化上面的情况呢?方法是使用尾递归。有尾递归优化的编译器会把尾递归编译成循环的形式,如果没有尾递归优化,那就自己写成循环的形式。如下面的例子所示:

//尾递归函数,返回一个函数调用,并且这个函数调用是自己
function haha(a, b) {
if(b) return b;
return haha(a, a-1);
} //优化成循环的形式
function yaya(a) {
let b = a;
while(b) {
b = b - 1;
}
}

需要注意的是,看上面尾递归的代码,有一点很重要,就是用一个b变量来存上一次递归的值。这是尾递归常用的方法。另外,其实上面尾递归的代码不需要变量b,但为了便于说明,所以我加上了变量b。

相互递归

但是关于递归还有一种形式,就是相互递归,如下面的代码所示:

function haha1(a) {
if(!a) return a;
return haha2(a-1);
} function haha2(a) {
if(!a) return a;
return haha1(a-1);
} haha1(100); //输出0
haha1(12345678); //输出Maximum call stack size exceeded

可以看到,当相互递归层数过多的时候,也会发生栈溢出的情况。

蹦床函数

蹦床函数就是解决上面问题的方法。

首先我们改写上面的相互递归函数:

function haha1(a) {
if(!a) return a;
return function() {
return haha2(a-1);
}
} function haha2(a) {
if(!a) return a;
return function() {
return haha1(a-1);
}
}

这个改写就是建立一个闭包来封装相互递归的函数,它的好处是由于不是直接的相互递归调用,所以不会把上一次的递归作用域推进栈中,而是把封装函数储存在堆里面,利用堆这个容量更大但读取时间更慢的储存形式来替代栈这个容量小但读取时间快的储存形式,用时间来换取空间

我们尝试使用一下上面的函数:

haha1(3)(); //输出一个函数
haha1(3)()()(); //输出0

通过上面的示例可以看到,如果参数不是3而是很大的一个数字的时候,我们就需要写很多个括号来实现调用很多次。为了简便,我们可以把这种调用形式写成函数,这就是蹦床函数。如下所示:

function trampoline(func, a) {
let result = func.call(func, a);
while(typeof result === 'function') {
result = result();
}
return result;
}

基本原理是一直调用,直到返回值不是一个函数为止

最后来使用蹦床函数:

trampoline(haha1, 12345678); //过一会儿就输出0

由于储存在堆中,所以耗时较长,过了一会儿才会输出0,但是并没有报栈溢出的错误

js函数式编程——蹦床函数的更多相关文章

  1. Python进阶之函数式编程(把函数作为参数)

    什么是函数式编程? 什么是函数式编程? 函数:function 函数式:functional,一种编程范式 函数式编程是一种抽象计算的编程模式 函数≠函数式,比如:计算≠计算机 在计算机当中,计算机硬 ...

  2. js函数式编程(一)-纯函数

    我将写的第一个主题是js的函数式编程,这一系列都是mostly adequate guide这本书的读书总结.原书在gitbook上,有中文版.由于原作者性格活泼,书中夹杂很多俚语,并且行文洒脱.中文 ...

  3. js函数式编程术语总结 - 持续更新

    参考文档1 参考文档2 函数式编程术语 高阶函数 Higher-Order Functions 以函数为参数的函数 返回一个函数的函数 函数的元 Arity 比如,一个带有两个参数的函数被称为二元函数 ...

  4. js函数式编程(二)-柯里化

    这节开始讲的例子都使用简单的TS来写,尽量做到和es6差别不大,正文如下 我们在编程中必然需要用到一些变量存储数据,供今后其他地方调用.而函数式编程有一个要领就是最好不要依赖外部变量(当然允许通过参数 ...

  5. JS函数式编程 - 概念

    最近在看Typescript,顺便看了一些函数式编程,然后半个国庆假期就没有了.做个笔记,分几个部分写吧. 最开始接触函数式编程的时候,第一个接触的概念就是高阶函数,和柯里化.咋一看,这不就是长期用来 ...

  6. js函数式编程

    最近在看朴灵的<深入浅出nodejs>其中讲到函数式编程.理解记录下 高阶函数 比较常见,即将函数作为参数,或是将函数作为返回值得函数. 如ECMAScript5中提供的一些数组方法 fo ...

  7. 1.python函数式编程-map函数

    编程方法论 面向过程 函数式 面向对象 面向过程 将编程过程拆分成多个步骤,在函数中按照每个步骤进行编程: 函数式编程 编程语言定义的函数+数学意义的函数 1.不可变,不用变量保存状态,不修改变量: ...

  8. day16-python之函数式编程匿名函数

    1.复习 #!/usr/bin/env python # -*- coding:utf-8 -*- name = 'alex' #name=‘lhf’ def change_name(): name= ...

  9. [Python3] 036 函数式编程 返回函数

    目录 函数式编程 之 返回函数 1. 引子 2. 闭包 closure 函数式编程 之 返回函数 函数可以返回具体的值 也可以返回一个函数作为结果 1. 引子 1.1 定义一个普通函数 >> ...

随机推荐

  1. BASEDIR

    1)正常写python程序会有一个可执行的bin.py文件,假如这个文件需要导入my_module里面定义的模块,应该怎么设置sys.path(此时可以直接导入), 因为bin和model属于同级目录 ...

  2. jquery 返回浏览器顶部

    经常在网页中看到有这样的现象,点击一个按钮,然后页面会跳到页面的中指定的位置,那这种效果是怎么实现的呢? 很多网页都有这种效果:返回顶部或者跳到不同的楼层(以下是天猫的效果) 实现原理: 1.我们来看 ...

  3. javascript:控制台详解

    javascript工具——浏览器控制台详解  大神这篇博客是写在2011年,主要介绍 “Firefox” 浏览器插件 “Firebug” 的操作,如今主流浏览器对控制台都已经提供了很好的支持.我自己 ...

  4. linux查看磁盘大小df命令

    df -h https://www.cnblogs.com/sparkdev/p/9273094.html

  5. R语言-地图

    1.maps包的map()函数 >map('world', fill = TRUE,col=heat.colors(10)) #世界地图 >map("state", i ...

  6. cdn贝四层协议配置端口映射TCP端口转发

    端口映射就是将外网主机的IP地址的一个端口映射到内网中一台机器,提供相应的服务.当用户访问该IP的这个端口时,服务器自动将请求映射到对应局域网内部的机器上.端口映射有动态和静态之分 1.安装好节点后初 ...

  7. Python自动化测试用例设计--自动化测试用例与手工测试用例区别与联系

    1. 前言 手工测试用例是针对手工测试人员,自动化测试用例是针对自动化测试框架,前者是手工测试用例人员应用手工方式进行用例解析,后者是应用脚本技术进行用例解析,两者最大的各自特点在于,前者具有较好的异 ...

  8. Django的rest_framework的视图之基于通用类编写视图源码解析

    我们上一篇博客讲解了如何使用mixins类实现rest_framework的视图,但是其中有很多的冗余的代码,我们这边在来优化一下 1.queryset的视图函数 首先看下对queryset操作的视图 ...

  9. thinkphp 视图(一)

    视图 View <?php namespace app\index\controller; class Index{ public function index(){ return view() ...

  10. java 遇到的问题

    1.list.sort(new Comparator<String>() { @override public int compare(String o1, String o2) { re ...