内容要点:

本节讨论的是一种函数变换技巧,即把一次完整的函数调用拆成多次函数调用,每次传入的实参都是完整实参的一部分,每个拆分开的函数叫做不完全函数(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 不完全函数》的更多相关文章

  1. 简单物联网:外网访问内网路由器下树莓派Flask服务器

    最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...

  2. 利用ssh反向代理以及autossh实现从外网连接内网服务器

    前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...

  3. 外网访问内网Docker容器

    外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...

  4. 外网访问内网SpringBoot

    外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...

  5. 外网访问内网Elasticsearch WEB

    外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...

  6. 怎样从外网访问内网Rails

    外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...

  7. 怎样从外网访问内网Memcached数据库

    外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...

  8. 怎样从外网访问内网CouchDB数据库

    外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...

  9. 怎样从外网访问内网DB2数据库

    外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...

  10. 怎样从外网访问内网OpenLDAP数据库

    外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...

随机推荐

  1. 【转】【WebService】.NET C# 创建WebService服务

    Web service是一个基于可编程的web的应用程序,用于开发分布式的互操作的应用程序,也是一种web服务 WebService的特性有以下几点: 1.使用XML(标准通用标记语言)来作为数据交互 ...

  2. 编写高性能SQL的注意事项

    在数据库部分,对数据库应用性能改进来说,需要重点关注应用程序,在查询设计和索引策略等方面进行优化,甚至可以把数据库查询效率提高数百倍,在其他方面的优化努力,其效果就没有这么明显(见下图).本文重点描述 ...

  3. Matlab最新的官方文档中文翻译

    文章翻译的是Matlab最新的官方文档R2016b,可能后续如果我还有时间会继续翻译,希望能够帮到大家,翻译的不好请大家不要吐槽. Matlab官方文档地址:http://cn.mathworks.c ...

  4. js 判断是否为正数

    function validate(num) { var reg = /^\d+(?=\.{0,1}\d+$|$)/   if(reg.test(num)) return true;   return ...

  5. webstrom30天免费试用期过后如何破解继续使用

    之前下了ws 直接就用了 也没有破解 30天过去了 老是提示你 神烦  网上找了一堆注册码什么的 终于发现一个良心网站 http://idea.qinxi1992.cn/ 步骤看下面的图

  6. one hot encoding

    转自:http://blog.sina.com.cn/s/blog_5252f6ca0102uy47.html 问题由来 在很多机器学习任务中,特征并不总是连续值,而有可能是分类值. 例如,考虑一下的 ...

  7. ios用storyboard快速创建静态cell

    在实际开发中经常会遇到下面这样的页面,通常我们用静态cell来做可以快速创建,提高效率 下面讲一下用storyboard创建方法,将一个tableViewController控制器拖入storyboa ...

  8. pack://application:,,,/

    FrameworkElementFactory gridFactory = new FrameworkElementFactory(typeof(Grid)); gridFactory.SetValu ...

  9. CSS3 六边形绘制

    把一个104px的div放在它们之间,设置一个背景颜色: width: 0; border-bottom: 30px solid #6C6; border-left: 52px solid trans ...

  10. hibernate事务控制

    在使用ssh中将事务委托给spring时老是出现事务不可用 经过检查,原因如下: 是因为在hibernate.cfg.xml文件中忘记进行了如下设置: hibernate.current_sessio ...