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 函数式编程
编程范式 编程范式是一个由思考问题以及实现问题愿景的工具组成的框架.很多现代语言都是聚范式(或者说多重范式): 他们支持很多不同的编程范式,比如面向对象,元程序设计,泛函,面向过程,等等. 函数式编程 ...
随机推荐
- 【Android应用开发】Android Studio 简介 (Android Studio Overview)
一. Intelij IDEA 环境简介 Android Studio 来源 : Android Studio 是 Intelij IDEA 的免费版本 + Android SDK 集成的; -- I ...
- 所谓 Spinner 组件
Spinner组件在平常的开发过程中应该算是比较常用的隶属于Input Control的android控件了,所以我们很有必要掌握这一个知识点,下面我就来谈一谈我对这个组件的一些认识. 是什么? 下拉 ...
- 后端分布式系列:分布式存储-MySQL 数据库事务与复制
好久没有写技术文章了,因为一直在思考 「后端分布式」这个系列到底怎么写才合适.最近基本想清楚了,「后端分布式」包括「分布式存储」和 「分布式计算」两大类.结合实际工作中碰到的问题,以寻找答案的方式来剖 ...
- 最简单的基于FFmpeg的视频编码器-更新版(YUV编码为HEVC(H.265))
===================================================== 最简单的基于FFmpeg的视频编码器文章列表: 最简单的基于FFMPEG的视频编码器(YUV ...
- UNIX网络编程——分析一帧基于UDP的TFTP协议帧
下图是UDP的段格式: 相比TCP段格式,UDP要简单得多,也没啥好说的,需要注意的是UDP数据长度指payload加上首部的长度. 下面分析一帧基于UDP的TFTP协议帧: 以太网首部 0000: ...
- Android Studio(AS)-->导入项目
1:首先,你必须要有一个工程(Project), 才可以打开项目(Module); (注意:Eclipse中的Workspace对应Android Studio 中的Project, Eclipse中 ...
- 【一天一道LeetCode】#93. Restore IP Addresses
一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Given a ...
- Unity2D Sprite灰白图(Unity3D开发之十一)
猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: http://www.cocos2dev.com/?p=596 昨晚看到群里问到2DSpr ...
- javascript之DOM编程正则表达式引入
在javascript中,正则表达式和java中区别不大.只有一小部分不同的地方: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Trans ...
- CSS引入
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...