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 函数式编程
编程范式 编程范式是一个由思考问题以及实现问题愿景的工具组成的框架.很多现代语言都是聚范式(或者说多重范式): 他们支持很多不同的编程范式,比如面向对象,元程序设计,泛函,面向过程,等等. 函数式编程 ...
随机推荐
- 【IOS 开发】基本 UI 控件详解 (UIDatePicker | UIPickerView | UIStepper | UIWebView | UIToolBar )
转载注明出处 : http://blog.csdn.net/shulianghan/article/details/50348982 一. 日期选择器 (UIDatePicker) UIDatePic ...
- Spark技术内幕:Master的故障恢复
Spark技术内幕:Master基于ZooKeeper的High Availability(HA)源码实现 详细阐述了使用ZK实现的Master的HA,那么Master是如何快速故障恢复的呢? 处于 ...
- 微软在线测试之lucky string,有关斐波那契的题目都在此了
解决方案: int _tmain(int argc,_TCHAR* argv[]) { size_t fib[] = {1,2,3,5,8,13,21,34}; string str,tempstr; ...
- Android简易实战教程--第十一话《获取手机所有应用信息Engine类详解》
如果想要获取系统手机应用的详细信息,那么下边代码可以直接作为模板使用.笔者对每一行代码都做了注解,供您参考.直接上代码: package com.example.itydl.engines; impo ...
- listview下拉刷新上拉加载扩展(二)-仿美团外卖
经过前几篇的listview下拉刷新上拉加载讲解,相信你对其实现机制有了一个深刻的认识了吧,那么这篇文章我们来实现一个高级的listview下拉刷新上拉加载-仿新版美团外卖的袋鼠动画: 项目结构: 是 ...
- J2EE学习从菜鸟变大鸟之六 EJB(Enterprise JavaBean)企业级Java组件
EJB (EnterpriseJavaBean)是J2EE的一部分,定义了一个用于开发基于组件的企业多重应用程序的标准.其特点包括网络服务支持和核心开发工具(SDK).其称为Java 企业Bean,是 ...
- Hadoop的运行痕迹
http://www.cnblogs.com/forfuture1978/archive/2010/11/23/1884967.html 一篇讲的很好的 hadoop 基本运行环境配置信息
- CentOS 64-bit下安装JDK和Tomcat并设置Tomcat开机启动操作步骤
准备文件如下: 1.CentOS-6.4-x86_64-bin-DVD1.iso 2.jdk-7u67-linux-x64.rpm 3.apache-tomcat-7.0.55.tar.gz 安装步骤 ...
- 从JDK源码角度看并发锁的优化
在CLH锁核心思想的影响下,JDK并发包以CLH锁作为基础而设计,其中主要是考虑到CLH锁更容易实现取消与超时功能.比起原来的CLH锁已经做了很大的改造,主要从两方面进行了改造:节点的结构与节点等待机 ...
- mysql删除重复数据只保留一条
mysql删除重复数据只保留一条 新建一张测试表: CREATE TABLE `book` ( `id` char(32) NOT NULL DEFAULT '', `name` varchar(10 ...