=====================================

函数的柯里化与反柯里化

[这是一篇比较久之前的总结了,若有错漏,请指正!]

柯里化 currying

维基百科的名词解释:柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。这个技术由 Christopher Strachey 以逻辑学家哈斯凯尔·加里命名的,尽管它是 Moses Schönfinkel 和 Gottlob Frege 发明的。

定义:

函数柯里化,是固定部分参数,返回一个接受剩余参数的函数,也称为部分计算函数,目的是为了缩小适用范围,创建一个针对性更强的函数。

特点:

提高了代码的合理性,更重的它突出一种思想---降低适用范围,提高适用性。
对于一个已有函数,对其约定好其中的某些参数输入,然后生成一个更有好的、更符合业务逻辑的函数。

柯里化(currying),我们可以这么理解:

柯里化就是一个函数在参数没给全时返回另一个函数,返回的函数的参数正好是余下的参数。
比如:你制定了x和y, 如2的3次方,就返回8, 如果你只制定x为2,y没指定, 那么就返回一个函数:2的y次方, 这个函数只有一个参数:y。这样就非常容易理解吧。
  1. 提高适用性
  2. 延迟执行
  3. 固定易变因素

    http://www.cnblogs.com/pigtail/p/3447660.html - 前端开发者进阶之函数反柯里化unCurrying

function currying(fn) {

var __args = Array.prototype.slice.call(arguments, 1);

return function () {

var __inargs = slice.call(arguments);

return fn.apply(null, __args.concat(__inargs));

};

}

提高适用性

    function square(i) {
return i * i;
} function dubble(i) {
return i *= 2;
} function map(handeler, list) {
return list.map(handeler);
} // 数组的每一项平方
map(square, [1, 2, 3, 4, 5]);
map(square, [6, 7, 8, 9, 10]);
map(square, [10, 20, 30, 40, 50]); // 数组的每一项加倍
map(dubble, [1, 2, 3, 4, 5]);
map(dubble, [6, 7, 8, 9, 10]);
map(dubble, [10, 20, 30, 40, 50]);
例子中,创建了一个map通用函数,用于适应不同的应用场景。显然,通用性不用怀疑。
同时,例子中重复传入了相同的处理函数:square和dubble。
应用中这种可能会更多。当然,【通用性的增强必然带来适用性的减弱】
    使用currying
function square(i) {
return i * i;
} function dubble(i) {
return i *= 2;
} function map(handeler, list) {
return list.map(handeler);
} var mapSQ = currying(map, square);
mapSQ([1, 2, 3, 4, 5]);
mapSQ([6, 7, 8, 9, 10]);
mapSQ([10, 20, 30, 40, 50]);
// ...... var mapDB = currying(map, dubble);
mapDB([1, 2, 3, 4, 5]);
mapDB([6, 7, 8, 9, 10]);
mapDB([10, 20, 30, 40, 50]);
// ......
【缩小了函数的适用范围,但同时提高了函数的适用性】

延迟执行

    var curry = function(fn){
var _args = []; return function(){
if(arguments.length === 0){
return fn.apply(this,_args);
} [].push.apply(_args,arguments); // console.log(arguments.callee);
return arguments.callee;
}
} var sum=0;
var add =function(){
for (var i = 0,c; c=arguments[i++];){
sum+=c;
}
return sum;
}; var add = curry(add); var res1 = add(1)(2)(3)();
console.log(res1)
// 6 // add(1);
// add(2);
// add(3);
// var res2=add();
// console.log(res2)
currying函数, 便可以延迟到最后一刻才一起计算, 好处不言而喻, 在很多场合可以避免无谓的计算, 节省性能, 也是实现惰性求值的一种方案.

固定易变因素

提前把易变因素,传参固定下来,生成一个更明确的应用函数。最典型的代表应用,是bind函数用以固定this这个易变对象。
Function.prototype.bind = function(context) {
var _this = this,
_args = Array.prototype.slice.call(arguments, 1);
return function() {
return _this.apply(context, _args.concat( Array.prototype.slice.call(arguments)));
}
} bind函数本身就是柯里化的一种体现,函数可以通过bind绑定新的上下文环境来改变其所处的上下文,并生成一个新的函数。这里第一次传入的参数_args就是函数本身的信息,被保存在了函数的闭包之中,就是柯里化的属性;之后的第二个参数arguments,就是在函数调用的过程中再传递的剩余参数。

反柯里化 uncurrying

反科里化的话题来自javascript之父Brendan Eich去年的一段twitter.

参考

http://www.jb51.net/article/32435.htm - javascript中有趣的反柯里化深入分析

http://sombie.diandian.com/post/2013-06-28/40050585369 -【WEB前端】由JavaScript反柯里化所想到的

定义

让你自定义的对象拥有原生JS对象的方法,并利用鸭子类型的特征扩展其使用范围。

作用

扩展函数适用范围的方法
把函数也当作普通数据来使用, 当函数名本身是个变量的时候, 这种调用方法特别方便.
扩大函数的适用性,使本来作为特定对象所拥有功能的函数可以对全体对象使用
Function.prototype.uncurry = function() {
var _this = this
return function() {
return Function.prototype.call.apply(_this, arguments)
}
}
短小精悍,科学上讲,浓缩的都是精品,但越精品的往往越难以理解。分解一下:
1 为Function原型添加unCurrying方法,这样所有的function都可以被借用;
2 返回一个借用其它方法的函数,这是目的;
3 借用call方法实现,但call方法参数传入呢?借用apply,至此完毕。

最终是把this.method转化成 method(this,arg1,arg2....)以实现方法借用和this的泛化。

用法

foo = somefun.uncurry();

foo(obj, args...) <==> obj.somefun(args)。

例:让Object也拥有push的方法

var push = Array.prototype.push.uncurry()

push({},'aa');

反柯里化的最简方式实现

//将原生的bind函数转换为全局的bind
var bind = Function.prototype.call.bind(Function.prototype.bind);
module={
debug:function(){
console.log.apply(console,arguments);
}
};
var debug = bind(module.debug,module); debug('adsfadsf','444',[1,2,3]);
//输出:adsfadsf 444 [1, 2, 3] //将原生的push函数转换为全局的push
var push = Function.prototype.call.bind([].push); var person ={
name:'aa',
age:33
};
push(person,'44'); debug(person);
//输出:Object {0: "44", name: "aa", age: 33, length: 1} 为什么push函数可以应用到非数组对象上?
v8引擎里面Array.prototype.push的代码
function ArrayPush() {
var n = TO_UINT32( this.length );
var m = %_ArgumentsLength();
for (var i = 0; i < m; i++) {
this[i+n] = %_Arguments(i); //属性拷贝
this.length = n + m; //修正length
return this.length;
}
}
ArrayPush方法没有对this的类型做任何显示的限制,所以理论上任何对象都可以被传入ArrayPush这个访问者。
push可以应该到类似数组类型的对象上,即鸭子类型

动态语言中重要的鸭子类型思想

故事:

很久以前有个皇帝喜欢听鸭子呱呱叫,于是他召集大臣组建一个一千只鸭子的合唱团。大臣把全国的鸭子都抓来了,最后始终还差一只。有天终于来了一只自告奋勇的鸡,这只鸡说它也会呱呱叫,好吧在这个故事的设定里,它确实会呱呱叫。 后来故事的发展很明显,这只鸡混到了鸭子的合唱团中。— 皇帝只是想听呱呱叫,他才不在乎你是鸭子还是鸡呢。

这个就是鸭子类型的概念,在javascript里面,很多函数都不做对象的类型检测,而是只关心这些对象能做什么。

Array构造器和String构造器的prototype上的方法就被特意设计成了鸭子类型。这些方法不对this的数据类型做任何校验。这也就是为什么arguments能冒充array调用push方法.

JS 函数的柯里化与反柯里化的更多相关文章

  1. js高阶函数应用—函数柯里化和反柯里化(二)

    第上一篇文章中我们介绍了函数柯里化,顺带提到了偏函数,接下来我们继续话题,进入今天的主题-函数的反柯里化. 在上一篇文章中柯里化函数你可能需要去敲许多代码,理解很多代码逻辑,不过这一节我们讨论的反科里 ...

  2. JS的防抖,节流,柯里化和反柯里化

    今天我们来搞一搞节流,防抖,柯里化和反柯里化吧,是不是一看这词就觉得哎哟wc,有点高大上啊.事实上,我们可以在不经意间用过他们但是你却不知道他们叫什么,没关系,相信看了今天的文章你会有一些收获的 节流 ...

  3. 在iframe里调用parent.func()引出的js函数运行在它们被定义的作用域里,而不是它们被执行的作用域里

    有个document里定义了一个函数func(),同时在document里嵌入了一个iframe,在这个iframe里调用父窗口的方法:parent.func(),本来我以为这个函数的运行环境是在这个 ...

  4. js高阶函数应用—函数柯里化和反柯里化

    在Lambda演算(一套数理逻辑的形式系统,具体我也没深入研究过)中有个小技巧:假如一个函数只能收一个参数,那么这个函数怎么实现加法呢,因为高阶函数是可以当参数传递和返回值的,所以问题就简化为:写一个 ...

  5. 简单粗暴详细讲解javascript实现函数柯里化与反柯里化

    函数柯里化(黑人问号脸)???Currying(黑人问号脸)???妥妥的中式翻译既视感:下面来一起看看究竟什么是函数柯里化: 维基百科的解释是:把接收多个参数的函数变换成接收一个单一参数(最初函数的第 ...

  6. JavaScript中的函数柯里化与反柯里化

    一.柯里化定义 在计算机科学中,柯里化是把 接受多个参数的函数 变换成 接受一个单一参数(最初函数的第一个参数)的函数 并且返回 接受余下参数且返回结果的新函数的技术 高阶函数 高阶函数是实现柯里化的 ...

  7. JS中的柯里化与反柯里化

    先占个位 看了一天折资料,感觉清楚多了

  8. 前端开发者进阶之函数反柯里化unCurrying

    函数柯里化,是固定部分参数,返回一个接受剩余参数的函数,也称为部分计算函数,目的是为了缩小适用范围,创建一个针对性更强的函数. 那么反柯里化函数,从字面讲,意义和用法跟函数柯里化相比正好相反,扩大适用 ...

  9. 浅析 JavaScript 中的 函数 uncurrying 反柯里化

    柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...

随机推荐

  1. fopen函数和fread函数、fwrite函数

    fopen(打开文件) 相关函数 open,fclose 表头文件 #include<stdio.h> 定义函数 FILE * fopen(const char * path,const ...

  2. [No0000AA]Windows 系统环境变量列表

    环境变量 详细信息 %ALLUSERSPROFILE% 所有用户 Profile 文件位置 %APPDATA% 应用程序数据的默认存放位置 %CD% 当前目录 %CLIENTNAME% 联接到终端服务 ...

  3. CSS中一些常见的兼容性问题

    CSS中一些兼容性问题就是浏览器兼容,而这些浏览器兼容问题主要是Ie和FF之间的争斗. CSS hack中的一些事: 我们为了让页面形成统一的效果,要针对不同的浏览器或不同版本写出对应可解析的CSS样 ...

  4. C++使用vector

    #include <iostream> #include <string> #include <vector> using namespace std; void ...

  5. MVC Nhibernate 示例

    首先,非常感谢提出问题的朋友们,使得本人又去深入研究了NHibernate的<Session-Per-Request 模式>.   前言: 谈到NHibernate大伙并不陌生,搞Java ...

  6. [LeetCode] Multiply Strings 字符串相乘

    Given two numbers represented as strings, return multiplication of the numbers as a string. Note: Th ...

  7. JsonResult类设置返回json的长度(工作笔记 json转化出错)

    public JsonResult PoundageReportSearch()         {    JsonResult jr = new JsonResult();        // 实例 ...

  8. 使用App.config管理数据库连接

    程序的数据库连接字符串可以保持在程序的配置文件App.config中,便于管理. 将配置文件添加至解决方案: 添加连接信息: <?xml version="1.0"?> ...

  9. 渗透测试-信息收集-c段收集

    平时做渗透测试我比较喜欢用lijiejie 写的 subDomainsBrute来爆破子域名 那么爆破完成后就想收集一下网站的c段信息 下面以平安为例 爆破得到子域名为 i.pingan.com.cn ...

  10. 取消ie浏览器edge浏览器输入框右边的叉和眼睛

    在ie高版本浏览器和edge浏览器里type为text和password的input框在输入时右边会出现×和眼睛,如果需要清除,方法如下: 首先在页面头部声明兼容性模式 <meta http-e ...