内容要点:

本节讨论的是一种函数变换技巧,即把一次完整的函数调用拆成多次函数调用,每次传入的实参都是完整实参的一部分,每个拆分开的函数叫做不完全函数(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. C#Windows Form简易计算器实现(上)

    第一次写博客,来分享一个简易计算器的代码.作为一名准程序员,就是要多写代码才能孰能生巧.重视基础知识才能飞的更快更高以及更稳. 代码可能会写的很糟糕,不完美不安全之处希望发现的越多越好 c#编写计算器 ...

  2. 【多重背包】HDU 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活

    Time Limit : 1000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) ...

  3. python中的编码声明

    python中的第一行,目的就是指出,你想要你的这个文件中的代码用什么可执行程序去运行它,就这么简单 #!/usr/bin/python 相当于写死了 python 路径(是告诉OS执行这个py时,调 ...

  4. python 数据清洗之字符串处理

    在数据分析中,特别是文本分析中,字符处理需要耗费极大的精力, 因而了解字符处理对于数据分析而言,也是一项很重要的能力. 字符串处理方法 首先我们先了解下都有哪些基础方法 首先我们了解下字符串的拆分sp ...

  5. scp命令报错(IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!)

    使用scp命令移动某一服务器的文件到另外的服务器时报出一下错误: [root@bogon vm_sys1]# scp project.tar.gz root@172.31.0.90:/webdata/ ...

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

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

  7. 解决vs2013下创建的python文件,到其他平台(如linux)下中文乱码(或运行时报SyntaxError: (unicode error) 'utf-8' codec can't decode byte...)

    Vs2013中创建python文件,在文件中没输入中文时,编码为utf-8的,如图 接着,在里面输入几行中文后,再次用notepad++查看其编码如下,在vs下运行也报错(用cmd运行就不会): 根据 ...

  8. glusterfs快速安装

    因为公司ES02集群使用的是SSD磁盘做的raid0,为了保证存放文件的可靠性,即在ES02集群上部署了一套分布式文件系统glusterfs.   结构 ES11    含有gfs程序,并挂载gfs在 ...

  9. 《css揭秘》

    <css揭秘> 第一章:引言 引言 案例们 第二章:背景与边框 背景和边框 半透明边框(rgba/hsla.background-clip) 多重边框(box-shadow) 灵活的背景定 ...

  10. UNIX基础--进程和守护进程

    进程和守护进程 Processes and Daemons 进程(Processes) FreeBSD 是一个多任务操作系统. 这就意味着好像一次可以运行一个以上的程序. 每个占用一定时间运行的程序就 ...