JavaScript中的Partial Application和Currying
这篇文章是一篇学习笔记,记录我在JS学习中的一个知识点及我对它的理解,知识点和技巧本身并不是我原创的。(引用或参考到的文章来源在文末)
先不解释Partial Application(偏函数应用)和Currying(加里化)的字面意思,从实际的示例入手会比较方便
比如有个function sum(a,b){return a+b}
function bindFirstArg(fn,n){//工厂不仅要接收改变的值,还要接收逻辑
return function(x){
return fn(n,x);
}
}
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
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/
最新文章
- BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]
- localstorage 的属性改变问题
- Oracle函数over(),rank()over()作用及用法--分区(分组)求和&; 不连续/连续排名
- eclipse怎样在线安装hibernate tools插件并使用
- 听声辨位识DUMP
- Spring aop与HibernateTemplate——session管理(每事务一次 Session)
- 将CGPoint、CGSize、CGRect等放进数组的方法
- 《SDN核心技术剖析和实战指南》3.3读书笔记
- 初级AD域渗透系列
- zoj 2770 Burn the Linked Camp
- Unable to make the module: related gradle configuration was not found. Please, re-import the Gradle project and try again
- Redis 2种持久化模式的缺陷
- 空间数据可视化之ArcLayer详解
- maven 项目快速下载jar方式
- oracle12 安装
- linux下/proc/diskstats文件详解
- nodejs学习笔记<;二>; 使用node创建基础服务器
- js 正则表达式:密码必须由6-12位数字加字母组成
- Android 判断是否能真正上网
- Visual Studio 2017 系统发布部署服务器教程
热门文章
- NPOI的源代码编译
- mysqld -install命令时出现install/remove of the service denied错误的原因和解决办法
- Lua从入门到精通
- poj 1850 code(组合数学)
- Oracle tnsname.ora 链接问题
- [POJ 3420] Quad Tiling
- Sql2005 全文索引详解
- e8_4输出菲波拉契数列的前10项
- POJ3087 Shuffle&#39;m Up 简单模拟
- Python脚本控制的WebDriver 常用操作 <;二十四>; 定位frame中的元素