javascript函数式编程一例分析
js像其他动态语言一样是可以写高阶函数的,所谓高阶函数是可以操作函数的函数。因为在js中函数是一个彻彻底底的对象,属于第一类公民,这提供了函数式编程的先决条件。
下面给出一个例子代码,出自一本js教程,功能是计算数组元素的平均值和标准差,先列出非函数式编程的一种写法:
var data = [1,1,3,5,5];
var total = 0;
for(var i = 0;i < data.length;i++)
total += data[i];
var mean = tatal/data.length; //平均数为3
//计算标准差
total = 0;
for(var i = 0;i < data.length;i++){
var deviation = data[i] - mean;
tatal += deviation * deviation;
}
var stddev = Math,.sqrt(total/(data.length-1));//标准差为2
为了使用函数式编程,我们预先定义一些帮助函数(helper functions):
//将类数组对象转换为真正的数组
function array(a,n){
return Array.prototype.slice.call(a,n||0);
}
//将函数实参传递至左侧
function partial_left(f){
var args = arguments;
return function(){
var a = array(args,1);
a = a.concat(array(arguments));
return f.apply(this,a);
};
}
//将函数的实参传递至右侧
function partial_right(f){
var args = arguments;
return function(){
var a = array(arguments);
a = a.concat(array(args,1));
return f.apply(this,a);
};
}
//该函数实参被用做模版,实参列表中的undefined值会被实际实参值填充。
function partial(f){
var args = arguments;
return function(){
var a = array(args,1);
var i = 0,j = 0;
for(;i<a.length;i++)
if(a[i] === undefined)
a[i] = arguments[j++];
a = a.concat(array(arguments,j));
return f.apply(this,a);
};
}
//返回一个函数类似于f(g())
function compose(f,g){
return function(){
return f.call(this,g.apply(this,arguments));
};
}
下面我们给出完全用函数式编程的js代码:
var data = [1,1,3,5,5];
var sum = function(x,y){return x+y;};
var product = function(x,y){return x*y;};
var neg = partial(product,-1);
var square = partial(Math.pow,undefined,2);
var sqrt = partial(Math.pow,undefined,0.5);
var reciprocal = partial(Math.pow,undefined,-1);
//好吧,高潮来鸟 :)
var mean = product(reduce(data,sum),reciprocal(data.length));
var stddev = sqrt(product(reduce(map(data,compose(square,partial(sum,neg(mean)))),sum),reciprocal(sum(data.length,-1))));
除了reduce和map函数,其他函数前面都给出了。reduce函数类似与ruby中的inject函数:
ary = (1..10).to_a
ary.inject(0) {|sum,i|sum + i} //结果为55
js的写法如下:
var ary = [1,2,3,4,5,6,7,8,9,10]
ary.reduce(function(sum,i){
return sum + i;
},0);
0为sum的初始值,如果省略则sum为数组第一个元素的值,这里可以省略。
map函数也很简单,类似与对数组的每一个元素做操作,然后返回一个经过操作后的数组,就以ruby代码为例,js代码与此类似:
a = (1..3).to_a; #数组[1,2,3]
a.map {|x| x*2} #返回新数组[2,4,6]
下面我们来分析下那一长串的代码:)
sum和product定义了元素相加和相乘的函数;
neg也是一个函数功能等价于:product(-1,x),即对x值求负;
square函数等价于:Math.pow(x,2),即计算x的平方值,注意这里partial的第二个参数是undefined,这意味着这里的形参会被第一个实参填补(见前面partial的代码);再说的明白点:square(x)功能等于Math.pow(x,2)。
sqrt函数和square类似,功能等价于:Math.pow(x,0.5),相当于计算x的开二次方。
最后一个函数reciprocal也没什么难度,等价于:Math.pow(x,-1),即计算x的负一次方,相当于计算x的倒数。
下面就是如何把上面各种函数揉捏在一起鸟 :)
先看平均值的计算,很简单:就是先计算数组元素的和然后乘上数组长度的倒数,即数组和/数组长度。
最后来看貌似很难的标准差,我们最好由内向外看:
先看包含neg的那层:
//等价于函数sum(-1 * mean + x)
partial(sum,neg(mean)
下面看compose函数:
//下面在源代码上做了等价替换,可以再次等价于:
//square(sum(-1*mean + x)),再次展开(我剥,我剥,我剥洋葱...):
//Math.pow(sum(-1*mean + x),2);
compose(square,sum(-1*mean + x))
接下来看map函数:
//很清楚吧!?即data中每一个元素都为一个x,将其传入后面的函数,然后返回一个计算后的新数组,即新数组中的每个元素的值是data中的每个元素加上data负的平均数,然后对其结果计算2次方的结果。
map(data,Math.pow(sum(-1*mean + x),2))
再接着看map外面的reduce函数:
//将前面新数组的每个元素值加起来。
reduce(map(...),sum)
然后看一下reciprocal函数:
//等价于求(data.length-1)的倒数
reciprocal(sum(data.length,-1))
再看外层的product函数:
//等价于新数组元素的和除以(data.length-1)
product(reduce(...),reciprocal(...))
最外层的sqrt表示对以上除法得出的结果求平方根;大家可以对照一下前面非函数编程的代码,是一样一样滴 :) 看似蛮怕人的一大坨代码,展开分析后难度立马将至零。如果各位看官最后表示还是未看明白,那完全是本猫语言表达能力的问题,欢迎提问。
解释完毕,打完收功,大功告成。
javascript函数式编程一例分析的更多相关文章
- 转:JavaScript函数式编程(三)
转:JavaScript函数式编程(三) 作者: Stark伟 这是完结篇了. 在第二篇文章里,我们介绍了 Maybe.Either.IO 等几种常见的 Functor,或许很多看完第二篇文章的人都会 ...
- 转: JavaScript函数式编程(二)
转: JavaScript函数式编程(二) 作者: Stark伟 上一篇文章里我们提到了纯函数的概念,所谓的纯函数就是,对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环 ...
- 转:JavaScript函数式编程(一)
转:JavaScript函数式编程(一) 一.引言 说到函数式编程,大家可能第一印象都是学院派的那些晦涩难懂的代码,充满了一大堆抽象的不知所云的符号,似乎只有大学里的计算机教授才会使用这些东西.在曾经 ...
- JavaScript 函数式编程读书笔记2
概述 这是我读<javascript函数式编程>的读书笔记,供以后开发时参考,相信对其他人也有用. 说明:虽然本书是基于underscore.js库写的,但是其中的理念和思考方式都讲的很好 ...
- JavaScript 函数式编程读书笔记1
概述 这是我读<javascript函数式编程>的读书笔记,供以后开发时参考,相信对其他人也有用. 说明:虽然本书是基于underscore.js库写的,但是其中的理念和思考方式都讲的很好 ...
- 一文带你了解JavaScript函数式编程
摘要: 函数式编程入门. 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有. 前言 函数式编程在前端已经成为了一个非常热门的话题.在最近几年里,我们看到非常多的应用程序代码库里大量使用着函 ...
- javascript函数式编程和链式优化
1.函数式编程理解 函数式编程可以理解为,以函数作为主要载体的编程方式,用函数去拆解.抽象一般的表达式 与命令式相比,这样做的好处在哪?主要有以下几点: (1)语义更加清晰 (2)可复用性更高 (3) ...
- JavaScript函数式编程(纯函数、柯里化以及组合函数)
JavaScript函数式编程(纯函数.柯里化以及组合函数) 前言 函数式编程(Functional Programming),又称为泛函编程,是一种编程范式.早在很久以前就提出了函数式编程这个概念了 ...
- javascript 函数式编程
编程范式 编程范式是一个由思考问题以及实现问题愿景的工具组成的框架.很多现代语言都是聚范式(或者说多重范式): 他们支持很多不同的编程范式,比如面向对象,元程序设计,泛函,面向过程,等等. 函数式编程 ...
随机推荐
- socket系列之服务器端socket——ServerSocket类
一般地,Socket可分为TCP套接字和UDP套接字,再进一步,还可以被分为服务器端套接字跟客户端套接字.这节我们先关注TCP套接字的服务器端socket,Java中ServerSocket类与之相对 ...
- 03_MyBatis基本查询,mapper文件的定义,测试代码的编写,resultMap配置返回值,sql片段配置,select标签标签中的内容介绍,配置使用二级缓存,使用别名的数据类型,条件查询ma
1 PersonTestMapper.xml中的内容如下: <?xmlversion="1.0"encoding="UTF-8"?> < ...
- 【Netty源码学习】EventLoopGroup
在上一篇博客[Netty源码解析]入门示例中我们介绍了一个Netty入门的示例代码,接下来的博客我们会分析一下整个demo工程运行过程的运行机制. 无论在Netty应用的客户端还是服务端都首先会初始化 ...
- 【翻译】Ext JS 6.2 早期访问版本发布
原文:Announcing Ext JS 6.2 Early Access 非常开心,Sencha Ext JS 6.2早期访问版本今天发布了.早期访问版本的主要目的是为了让大家进行测试并评估Ext ...
- 【一天一道LeetCode】#202. Happy Number
一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Write a ...
- C++对C的函数拓展 - 默认参数
1 C++中可以在函数声明时为参数提供一个默认值, 当函数调用时没有指定这个参数的值,编译器会自动用默认值代替 void myPrint(int x = 3) { printf("x:%d& ...
- J2EE学习从菜鸟变大鸟之六 EJB(Enterprise JavaBean)企业级Java组件
EJB (EnterpriseJavaBean)是J2EE的一部分,定义了一个用于开发基于组件的企业多重应用程序的标准.其特点包括网络服务支持和核心开发工具(SDK).其称为Java 企业Bean,是 ...
- 【一天一道LeetCode】#117. Populating Next Right Pointers in Each Node II
一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Follow ...
- ADO.NET之Parameter属性
在ADO.NET中, public SqlParameterCollection Parameters {get;}会得到一个SqlParameter属性.下面通过一个例子进行详细的认识吧. //例如 ...
- java设计模式---调停者模式
中介者模式(Mediator):用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互. 通用类图: 举例:在一个公司里面 ...