这里是用 JavaScript 做的逆转序列(数组/字符串)的递归/尾递归实现。另外还尝鲜用了一下 ES6 的destructuring assignment + spread operator 做了一个更 functional 的版本(只支持数组)。

正确性能通过测试(参见 放在我 Github 上的 demo,顺手写了一个小小的测试框架),不过效率就要打问号了——特别是用了 ES6 特性的版本。这里主要是写来玩 JS 的函数式特性的。

1. 逆转序列的递归实现

先用 Haskell 实现做草稿(因为比较自然),数组版长这样

reverse' :: [a] -> [a]
reverse' [] = []
reverse' (x:xs) = reverse' xs ++ [x]

在 JavaScript 里,数组和字符串都有 sliceconcat,所以可以写成两种“序列”(在离散数学之类的领域里经常把两者合称为 sequence) 都支持的版本。

Note:

均出现于 ECMAScript 3,实现于 JavaScript 1.2

对应的 JS 实现如下,把 base case 设定到序列长度小于 1 的时候,这样缩减了一步递归。相比而言还是 Haskell 版比较一目了然啊……

function recursiveReverse(seq) {
return seq.length > 1 ?
recursiveReverse(seq.slice(1)).concat(seq.slice(0, 1)) : seq;
}

经过测试,数组和字符串都能通过。

2. 尾递归实现

Haskell 版本长这样,用一个 ret 传递已完成的部分:

reverse' :: [a] -> [a]
reverse' list = rev list []
where rev [] ret = ret
rev (x:xs) ret = rev xs (x:ret)

对应的 JS 版本,这里用 slice(0, 0) 来避开类型检查创建空序列。

function tailReverse(seq) {
return (function rev(seq, ret) {
return seq.length > 0 ?
rev(seq.slice(1), seq.slice(0, 1).concat(ret)) : ret;
})(seq, seq.slice(0, 0));
}

经过测试,数组和字符串都能通过。

3. 使用 ES6 新特性的递归版本

ES6 引入了 destructuring assignmentspread operator,这样就可以部分地使用函数式编程中的模式匹配(pattern matching)。不过我在 ES6 的新特性里还没找到能在语法层面上让 JS 的函数像 Haskell 那样将模式匹配融合进重载(overloading)的组合(destructuring assignment 本身就是模式匹配的一个子集而已),要重载还是得用当前 JS 里最常见的解决办法——if-else或者三目运算符配合类型检查。这里因为用法简单,直接三目运算符就行。

配合 ES6 新特性实现的简单递归版如下,因为 JS 里数组和字符串没有通用的功能类似 append (往后加元素并返回新的数组)的函数,懒得折腾类型检查了,所以这里只实现了数组版(由于 destructuring assignment + spread operator 的组合[x, ...xs]会将字符串分割成第一个字符,[其他字符组成的数组],因此直接给下面的函数传入字符串之后再将返回值 join 回字符串也可以达到一样的效果)。

function pmReverse([x, ...xs]) {
return typeof x === "undefined" ? [] : pmReverse(xs).concat([x]);
}

经过测试,数组都可以正常逆转(只有在浏览器打开 ES6 特性的时候这个页面才会正常跑掉这个版本的测试,不然就只有一个 alert)。

4. 使用 ES6 新特性的尾递归版本

function pmTailReverse(list) {
return (function rev([x, ...xs], ret) {
return typeof x === "undefined" ? ret : rev(xs, [x].concat(ret));
})(list, []);
}

经过测试,数组都可以正常逆转(只有在浏览器打开 ES6 特性的时候这个页面才会正常跑掉这个版本的测试,不然就只有一个 alert)。

关于测试页面

为了方便写了一个小小的测试框架,参见源代码

因为不支持 ES6 的浏览器在解析 ES6 特性的代码时会出语法错误,用 try-catch 抓不了,这里用了 window.onerror 来捕捉并提示。不知为何我的 Chrome 开了实验性 JS 依然打不开 ES6 支持…… FireFox 倒是默认就能用。

逆转序列的递归/尾递归(+destructuring assignment)实现(JavaScript + ES6)的更多相关文章

  1. Destructuring Assignment in JS(解构assignment in js)

    Destructuring Assignment In JavaScript 更省事,代码显得也清楚. Arrays 传统的声明赋值: let johnDoe = ["John", ...

  2. 解构赋值 Destructuring Assignment

    解构赋值 Destructuring Assignment ES6中可以通过一定的模式将数组或对象中的值直接赋值给外部变量,称为解构 对象的解构赋值 // 在ES5中,当需要获取一个对象中的变量值的时 ...

  3. destructuring assignment

    [destructuring assignment] The destructuring assignment syntax is a JavaScript expression that makes ...

  4. [ES6] 08. Destructuring Assignment -- 1

    Here is the way you get value from an object: var obj = { color: "blue" } console.log(obj. ...

  5. ES6 Destructuring Assignment All In One

    ES6 Destructuring Assignment All In One ES6 & Destructuring Assignment Axios, vue https://develo ...

  6. Object Destructuring Assignment vs Object.assign

    Object Destructuring Assignment vs Object.assign // const params = Object.assign({}, this.$route.par ...

  7. js destructuring assignment bug

    js destructuring assignment bug https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Op ...

  8. 斐波那契数列 递归 尾递归 递推 C++实现

    ==================================声明================================== 本文原创,转载请注明作者和出处,并保证文章的完整性(包括本 ...

  9. kotlin递归&尾递归优化

    递归: 对于递归最经典的应用当然就是阶乘的计算啦,所以下面用kotlin来用递归实现阶乘的计算: 编译运行: 那如果想看100的阶乘是多少呢? 应该是结果数超出了Int的表述范围,那改成Long型再试 ...

随机推荐

  1. webpy使用笔记(二) session/sessionid的使用

    webpy使用笔记(二) session的使用 webpy使用系列之session的使用,虽然工作中使用的是django,但是自己并不喜欢那种大而全的东西~什么都给你准备好了,自己好像一个机器人一样赶 ...

  2. 使用grunt打包ueditor源代码

    支持版本支持 UEditor 1.3.0+ 的版本 使用方法1.线上下载ueditor下载地址:ueditor,要下载"完整版 + 源码" 2.安装nodejs下载nodejs并安 ...

  3. 将doc文件批量转为pdf文件

    需要将不少doc文件转为pdf,WPS带有这种功能,但是鼠标点击次数太多以后整个人都变得很烦躁 用了一下午去搜这方面的工具软件,找到若干.有一些免费,有一些试用的,但总归就找到一个真正能用,虽说生成的 ...

  4. Jenkins + GitHub + fir-cli 一行命令从源码到fir.im

    上周简书作者宣X_x  分享了一篇文章--用Jenkins+GitHub+Xcode+fir搭了一个持续集成环境,整个记录见(传送门). _______ 其实fir.im为我们提供了一个更简单的方式: ...

  5. Java日期与时间的处理/Date,String,Calendar转换

    public class Demo01 { //Java中Date类和Calendar简介 public static void main(String[] args) { long now=Syst ...

  6. Django基础——Form&Ajax篇

    一 Form 在实际的生产环境中,登录和注册时用户提交的数据浏览器端都会使用JavaScript来进行验证(比如验证输入是否为空以及输入是否合法),但是浏览器可能会禁用JavaScirpt,同时也有人 ...

  7. 定义一个时钟类——Clock,它包括三个int型 成员变量分别表示时、分、秒,一个构造方法用于对三个成员变量(时、分、秒) 进行初始化,还有一个成员方法show()用于显示时钟对象的时间。其次,再定义 一个主类——TestClass,在主类的main方法中创建多个时钟类的对象,使用这 些对象调用方法show()来显示时钟的时间

    package java1; public class Clock { int hhh; int mmm; int sss; Clock(int h,int m,int s) { hhh=h; mmm ...

  8. c++builder调用VC的dll以及VC调用c++builder的dll

    解析__cdecl,__fastcall, __stdcall 的不同:在函数调用过程中,会使用堆栈,这三个表示不同的堆栈调用方式和释放方式. 比如说__cdecl,它是标准的c方法的堆栈调用方式,就 ...

  9. 如何关闭Linux里边的selinux ?

    原文地址: http://jingyan.baidu.com/article/6d704a131ba67828da51ca73.html 有很多的Linux使用者因为对selinux不熟悉,所以都会将 ...

  10. nginx是一个反向代理的软件

    nginx只是一个反向代理的软件,和语言无关,理论上支持任何Web平台,当然http://Asp.net也不例外,http://51aspx.com就是http://Asp.net开发的,前端暴漏的是 ...