《JS权威指南学习总结--8.8.3 不完全函数》
内容要点:
本节讨论的是一种函数变换技巧,即把一次完整的函数调用拆成多次函数调用,每次传入的实参都是完整实参的一部分,每个拆分开的函数叫做不完全函数(partial function),每次函数调用叫做不完全调用(partial application),这种函数变换的特点是每次调用都返回一个函数,直到得到最终运行结果为止,举一个简单的例子,将对函数f(1,2,3,4,5,6)的调用修改为等价的f(1,2)(3,4)(5,6),后者包含三次调用,和每次调用相关的函数就是"不完全函数"。
一.
1.函数f()的bind()方法返回一个新函数,给新函数传入特定的上下文和一组指定的参数,然后调用f()。
我们说它把函数 "绑定至" 对象并传入一部分参数。bind()方法只是将实参放在(完整实参列表的)左侧,也就是说传入bind()的实参都是放在传入原始函数的实参列表开始的位置,但有时我们期望将传入bind()的实参放在(完整实参列表的)右侧:
//实现一个工具函数将类数组对象(或对象)转换为真正的数组
//在后面的示例代码中用到了这个方法将arguments对象转换为真正的数组
function array(a,n){ return Array.prototype.slice.call(a,n||0); }
//这个函数的实参传递至左侧
function partialLeft(f /*,...*/){
var args = arguments; //保存外部的实参数组
return function(){ //并返回这个函数
var a= array(args,1); //开始处理外部的第1个args
a = a.concat(array(arguments)); //然后增加所有的内部实参
return f.apply(this,a); //然后基于这个实参列表调用f()
};
}
//这个函数的实参传递至右侧
function partialRight(f/*,...*/){
var args = arguments; //保存外部实参数组
return function(){
var a = array(arguments); //从内部参数开始
a = a.concat(array(args,1)); //然后从外部第1个args开始添加
return f.apply(this,a); //最后基于这个实参列表调用f()
};
}
//这个函数的实参被用做模板
//实参列表中的undefined值都被填充
function partial(f/*,...*/){
var args = arguments; //保存外部实参数组
return function(){
var a = array(args,1); //从外部args开始
var i = 0,j =0;
//遍历args,从内部实参填充undefined值
for(;i<a.length;i++)
if(a[i] === undefined) a[i] = arguments[j++];
//现在将剩下的内部实参都追加进去
a = a.concat(array(arguments,j))
return f.apply(this,a);
};
}
//这个函数带有三个实参
var f = function(x,y,z){ return x*(y-z); };
//注意这三个不完全调用之间的区别
partialLeft(f,2)(3,4) //=>-2:绑定第一个实参:2*(3 - 4
partialRight(f,2)(3,4) //=>6:绑定最后一个实参:3*(4 -2)
partial(f,undefined,2)(3,4) //=>-6:绑定中间的实参:3*(2-4)
二.利用已有的函数来定义新的函数
利用这种不完全函数的编程技巧,可以编写一些有意思的代码,利用已有的函数来定义新的函数:
var increment = partialLeft(sum,1);
var cuberoot = partialRight(Math.pow,1/3);
String.prototype.first = partial(String.prototype.charAt,0);
String.prototype.last = partial(String.prototype.substr,-1,1);
当将不完全调用和其他高阶函数整合在一起的时候,事情就变得格外有趣了。比如,这里的例子定义了not()函数,它用到了刚才提到的不完全调用:
var not = partialLeft(compose,function(x){ return !x;});
var even = function(x){ return x%2 ==0; };
var odd = not(even);
var isNumber = not(isNaN);
我们也可以使用不完全调用的组合来重新组织平均数和标准差的代码,这种编程风格是非常纯粹的函数式编程:
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, .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))));
console.log(mean);//datad的平均数 3
console.log(neg(mean));//平均数*(-1): -2 :mean*-1
console.log(partial(sum,neg(mean)));//sum里的y是-3:即是负的平均数
console.log(compose(square,partial(sum,neg(mean))));//sum里的y是-3:函数式:(data-3)^2,compose:组合两个函数,见8.8.2高阶函数
console.log(Array.map(data,compose(square,partial(sum,neg(mean)))));//[4, 4, 0, 4, 4]: 即是平方差
console.log(Array.reduce(Array.map(data,compose(square,partial(sum,neg(mean)))),sum));//16:即是平方差的求和
console.log(reciprocal(sum(data.length,-1))); //0.25
console.log(product(Array.reduce(Array.map(data,compose(square,partial(sum,neg(mean)))),sum),reciprocal(sum(data.length,-1)))); //4
console.log(sqrt(product(Array.reduce(Array.map(data,compose(square,partial(sum,neg(mean)))),sum),reciprocal(sum(data.length,-1)))));//2
细细体会,跟数学式子差不多!
《JS权威指南学习总结--8.8.3 不完全函数》的更多相关文章
- 简单物联网:外网访问内网路由器下树莓派Flask服务器
最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...
- 利用ssh反向代理以及autossh实现从外网连接内网服务器
前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...
- 外网访问内网Docker容器
外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...
- 外网访问内网SpringBoot
外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...
- 外网访问内网Elasticsearch WEB
外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...
- 怎样从外网访问内网Rails
外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...
- 怎样从外网访问内网Memcached数据库
外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...
- 怎样从外网访问内网CouchDB数据库
外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...
- 怎样从外网访问内网DB2数据库
外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...
- 怎样从外网访问内网OpenLDAP数据库
外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...
随机推荐
- 2015 ACM / ICPC 亚洲区域赛总结(长春站&北京站)
队名:Unlimited Code Works(无尽编码) 队员:Wu.Wang.Zhou 先说一下队伍:Wu是大三学长:Wang高中noip省一:我最渣,去年来大学开始学的a+b,参加今年区域赛之 ...
- CodeVS 3415-最小和
原题 题目描述 Description 小浣熊松松来到文具店,选择了K支自己喜欢的水彩笔,并抄下了它们的价格.可是到结算时,他发现自己抄价格时抄得太密集,以至于所有价格连成了一个数字串(你可 ...
- iOS软件架构——架构模式(Architectural Pattern)
一个架构模式描述软件系统里的基本的结构组织或纲要.架构模式提供一些事先定义好的子系统,指定它们的责任,并给出把它们组织在一起的法则和指南.有些作者把这种架构模式叫做系统模式[STELTING02]. ...
- 双数组trie树的基本构造及简单优化
一 基本构造 Trie树是搜索树的一种,来自英文单词"Retrieval"的简写,可以建立有效的数据检索组织结构,是中文匹配分词算法中词典的一种常见实现.它本质上是一个确定的有限状 ...
- 【C#】面试题整理
1.C#中类是否支持多继承?请说明原因.答:不支持,需要用接口来实现多继承 2.我们都知道一个类可以有多个构造函数,并且C#会在我们创建类的时候默认的提供一个无参的构造函数,当我实现了另外一个有参数的 ...
- New : HTML5 中的新标签
基础 标签 描述 <!DOCTYPE> 定义文档类型. <html> 定义 HTML 文档. <title> 定义文档的标题. <body> 定义文档 ...
- centos 6.5 安装mysql 5.6错误
yum list libaio yum install libaio.i686 yum list glibc* yum install glibc.i686 yum list libstdc++* y ...
- 工具-Quick time播放器
拍屏的视频.素材视频等,用Quick time观看 1.左下角可切换时间/帧编号,直观看到某pose的帧位置: 2.播放进度条上有卡尺,可选择部分视频,显示-仅播放所选部分,点上显示-循环,可以反复观 ...
- ubuntu_虚拟机和SD卡链接失败,可能的原因
这个问题很简单吧,但是自己解决却用了很长时间,说一下方法吧! 1.有的虚拟机不兼容USB3.0的接口,所以在接SD卡(读卡器)时,请将读卡器拔出,插入笔记本USB2.0的接口上(当时自己没注意到这点, ...
- Func,Action 的介绍
Func,Action 的介绍 Func是一种委托,这是在3.5里面新增的,2.0里面我们使用委托是用Delegate,Func位于System.Core命名空间下,使用委托可以提升效率,例如在反射中 ...