这篇文章是一篇学习笔记,记录我在JS学习中的一个知识点及我对它的理解,知识点和技巧本身并不是我原创的。(引用或参考到的文章来源在文末)

  先不解释Partial Application(偏函数应用)和Currying(加里化)的字面意思,从实际的示例入手会比较方便

比如有个function sum(a,b){return a+b}

如果我们想固定一个值,就要先设定这个值:
var a=1;
sum(a,1);  sum(a,100);….
或者定义一个新函数 function newSum(x){return sum(1,x)}
如果要改变固定值呢,就需要定义多个新函数: function newSum1(x){return sum(,x)} , function newSum2(x){return sum(,x)}
或者建一个工厂方法 function makeSum(n){return function(x){return sum(n+x);}}  就可以 var sum10=makeSum(10); sum10(1);
可是这个工厂方法只能产出sum的逻辑,需要一个工厂能够输入什么逻辑,产出这个逻辑,因此可以改进为:
function bindFirstArg(fn,n){//工厂不仅要接收改变的值,还要接收逻辑
return function(x){
return fn(n,x);
}
}

  

可以这么用 
function add(a,b){return a+b;}
function del(a,b){return a-b;}
var add10=bindFirstArg(add,10);add10(1);
var del10=bindFirstArg(del,10);del10(1);
 
但是,当可变的参数不止一个是多个,并且个数也不固定的时候,怎么办?
这就是所谓的Partial Application了。
Partial application can be described as taking a function that accepts some number of arguments, binding values to one or more of those arguments, and returning a new function that only accepts the remaining, un-bound arguments.

简单来说就是如果我们有函数是多个参数的,我们希望能固定其中某几个参数的值。(比如上文中的newSum1就是sum函数的一个偏函数应用)

function partial(fn/*,args…..*/){
var args=Array.prototype.slice.call(arguments,1);//arguments是类数组对象,有length,但是没方法,所以用array的方法切割之,把第一个元素即this去掉
return function(){
var nargs=Array.prototype.slice.call(arguments,0);//也是将arguments转为数组,此arguments非上一个arguments
fn.apply(this,args.contact(nargs));
}
}

代码并不复杂,用法如下:

function sum(){
var args=Array.prototype.slice.call(arguments,0),n=0;
for(var i=0;i<args.length;i++){n+=args[i]}
return i;
}
sum(1,2,3);//普通调用,输出6
var p=partial(sum,10); p(1,2,3);//输出16
这么调用之所以可以成功是因为sum函数内部是用arguments来判断传入参数的,而没有写死,如果sum写死参数个数(function sum(a,b){return a+b;}),就会出问题,多于传入的参数会被忽略。
目前的partial是将参数一次顺序合并(contact),如果是想固定替换一些参数,可以改进为:
var partialAny=(function(){
var slice=Array.prototype.slice;//用立即执行函数加闭包先缓存这个方法
partialAny._={};//这个是用来标示替换的标志
return function(fn/*,args…..*/){//这个return的fn相当于就是partial
var orginArgs=slice.call(arguments,1);//partial接收的参数
return function(){//这个return的fn是最终调用的函数,里边执行了原函数的逻辑
var newArgs=slice.call(arguments,0);//
var args=[];
for(var i=0;i<orginArgs.length;i++)
{
args[i]=orginArgs[i]===partialAny._?newArgs.shift():orginArgs[i];
}
return fn.apply(this,args.contact(newArgs));
}
})();

这个稍微复杂一点,不过看过注释也比较容易明白几次return之间的关系,这里的标示替换的标志“._”略难理解,看完示例就很容易明白了:

function hex(r, g, b) { return '#' + r + g + b; }
hex('11', '22', '33'); // "#112233"
var __ = partialAny._;
var redMax = partialAny(hex, 'ff', __, __);
redMax('11', '22'); // "#ff1122"

接下来是Currying,它和Partial Application十分相似,不过它是用来解决另一个问题的:如何用多个单参数函数实现一个多参数函数。

Currying can be described as transforming a function of N arguments in such a way that it can be called as a chain of N functions each with a single argument.

就是说它在接收参数后会判断参数是否满足了原函数所需要的个数,如果不满足,会返回一个函数,其不够的参数仍然有待输入,如果够了,就直接调用原函数。

function curry(fn,n){
if(typeof n !==’number’){n=fn.length;}//如果没有认为指定待传的参数个数,就取原函数需要的参数个数
function getCurriedFn(prev){
return function(arg){
var args=prev.contact(arg);
if(args.length<n){
return getCurriedFn(args);
}
else{
return fn.apply(this,args);
}
}
}
return getCurriedFn([]);
}

代码本身也不难理解,其用法如下:

var i = 0;
function a(arg1, arg2, arg3) { return ++i + ': ' + arg1 + ', ' + arg2 + ', ' + arg3; }
// Normal function invocation.
a('x', 'y', 'z'); // "1: x, y, z"
a('x', 'y'); // "2: x, y, undefined"
a('x'); // "3: x, undefined, undefined"
a(); // "4: undefined, undefined, undefined"
// Curried function invocation.
var b = curry(a);
b(); // `a` not invoked, curried function returned
b('x'); // `a` not invoked, curried function returned
b('x')('y'); // `a` not invoked, curried function returned
b('x')('y')('z'); // "5: x, y, z"
b('x')('y')(); // "6: x, y, undefined"
b('x')()(); // "7: x, undefined, undefined"
b()('y')(); // "8: undefined, y, undefined"
b()()('z'); // "9: undefined, undefined, z"
b()()(); // "10: undefined, undefined, undefined”

偏函数应用和函数加里化在JS中应用是很少的,因为JS本身就支持多参数函数了,在一些其他语言如Haskell中,多参数函数都是通过加里化实现的,这里能够学习和理解这种思想和技巧,对于学习程序开发有一定的作用。

  这里只是我的一点学习笔记,感谢分享相关知识的前辈和大牛们,他们的文章地址如下:

http://benalman.com/news/2012/09/partial-application-in-javascript/

http://alecb.me/blog/currying-partial-application/

JavaScript中的Partial Application和Currying的更多相关文章

  1. 偏函数应用(Partial Application)和函数柯里化(Currying)

    偏函数应用指的是固化函数的一个或一些参数,从而产生一个新的函数.比如我们有一个记录日志的函数: 1: def log(level, message): 2: print level + ": ...

  2. Currying vs Partial Application

    柯里化相当于函数重构: 偏函数相当于函数适配. So, what is the difference between currying and partial application? As we s ...

  3. 浅析 JavaScript 中的 函数 currying 柯里化

    原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字 ...

  4. [Functional Programming] From simple implementation to Currying to Partial Application

    Let's say we want to write a most simple implementation 'avg' function: const avg = list => { let ...

  5. [Functional Programming] Create Reusable Functions with Partial Application in JavaScript

    This lesson teaches you how arguments passed to a curried function allow us to store data in closure ...

  6. JavaScript中的bind方法及其常见应用

    一.bind()方法的实现 在JavaScript中,方法往往涉及到上下文,也就是this,因此往往不能直接引用.就拿最常见的console.log("info…")来说,避免书写 ...

  7. javascript中apply、call和bind的区别及方法详解

    文章目录   apply.call apply.call 区别 apply.call实例 数组之间追加 获取数组中的最大值和最小值 验证是否是数组(前提是toString()方法没有被重写过) 类(伪 ...

  8. 函数式编程之-定义能够支持Partial application的函数

    是时候介绍如何在F#中定义函数了,在你没有接触过函数式编程语言之前,你也许会觉得C#/Java的语法已经够丰富了,有什么任务做不了呢?当你读过函数式编程之Currying和函数式编程之Partial ...

  9. 函数式编程之-Partial application

    上一篇关于Currying的介绍,我们提到F#是如何做Currying变换的: let addWithThreeParameters x y z = x + y + z let intermediat ...

随机推荐

  1. Android开发之点击两次Back键退出App

    Back按键的方法是onKeyDown()方法,重写该方法就可以改变back按键的作用. 实现点击两次Back按键退出app,有两种方法: 方法1. private static boolean is ...

  2. 使用SAE部署Flask,使用非SAE flask版本和第三方依赖包的方法

    目前SAE的Flask的版本为0.7,但是我从学习开始的flask版本就已经是0.10了,而且一些扩展都是使用的0.10以后的from flask.ext.特性进行引入的.所以需要修改SAE的环境. ...

  3. sublime text格式化插件

    sublime text 软件其实是自带格式化插件的,但是它默认的格式化插件,不太好用,且没有快捷键(虽然自己可以设置). 其默认的格式化是在 Edit  ->  Line  ->  Re ...

  4. Hibernate数据库持久层框架

    Hibernate是一种Java语言下的对象关系映射解决方案. 它是使用GNU宽通用公共许可证发行的自由.开源的软件.它为面向对象的领域模型到传统的关系型数据库的映射,提供了一个使用方便的框架.Hib ...

  5. volicety常用方法

    1.volicety得到某个元素的个数 $extendsInfos.size() 2.volicety 布尔值判断: 如果a为null,#if($a) ,产生的判断值是false  等同于#if(fa ...

  6. Innodb 锁系列2 事务锁

    上一篇介绍了Innodb的同步机制锁:Innodb锁系列1 这一篇介绍一下Innodb的事务锁,只所以称为事务锁,是因为Innodb为实现事务的ACID特性,而添加的表锁或者行级锁. 这一部分分两篇来 ...

  7. [原]Unity3D深入浅出 - 导航网格自动寻路(Navigation Mesh)

    NavMesh(导航网格)是3D游戏世界中用于实现动态物体自动寻路的一种技术,将游戏中复杂的结构组织关系简化为带有一定信息的网格,在这些网格的基础上通过一系列的计算来实现自动寻路..导航时,只需要给导 ...

  8. 【 D3.js 高级系列 — 5.1 】 颜色插值和线性渐变

    颜色插值指的是给出两个 RGB 颜色值,两个颜色之间的值通过插值函数计算得到.线性渐变是添加到 SVG 图形上的过滤器,只需给出两端的颜色值即可. 1. 颜色插值 在[高级 - 第 5.0 章]里已经 ...

  9. js基础第七天

    短信发送验证倒计时案例(常用) 关闭定时器clearInterval this 指向的是 事件的调用者 ,或者是函数的使用者. button 不可以用 disabled 不可用的意思 意思是变成灰色不 ...

  10. php数组使用小结

    在PHP中,数组分为两类:索引数组和关联数组.二者可以单独使用,也可以混合使用. 1.一维数组 一维数组的定义也很简单,常用的有如下两种方式: 1.1 直接赋值 1: <?php 3: $dwq ...