函数柯⾥化的意义_如何理解函数的柯⾥化
元宵节⼀过,年味⼉也就随之消散了,⼤家还是静下⼼来好好写代码吧,今天带⼤家来认识⼀下函数的柯⾥化,希望能帮到有需要的同学。
我们先从概念开始
什么是柯⾥化
柯⾥化,是函数式编程的⼀个重要概念。它既能减少代码冗余,也能增加可读性。另外,附带着还能⽤来装逼。
先给出柯⾥化的定义:在数学和计算机科学中,柯⾥化是⼀种将使⽤多个参数的⼀个函数转换成⼀系列使⽤⼀个参数的函数的技术。
柯⾥化的定义,理解起来有点费劲。为了更好地理解,我们看⼀下例⼦:
functionsum(a,b,c){
(a+b+c);
}
sum(1,2,3);//6
毫⽆疑问,sum是个简单的累加函数,接受3个参数,输出累加的结果。
假设有这样的需求,sum的前2个参数保持不变,最后⼀个参数可以随意。那么就会想到,在函数内,是否可以把前2个参数的相加过程,
给抽离出来,因为参数都是相同的,没必要每次都做运算。
如果先不管函数内的具体实现,调⽤的写法可以是这样:sum(1,2)(3);或这样sum(1,2)(10);。就是,先把前2个参数的运算结果拿到
后,再与第3个参数相加。
这其实就是函数柯⾥化的简单应⽤。
柯⾥化的实现
sum(1,2)(3);这样的写法,并不常见。拆开来看,sum(1,2)返回的应该还是个函数,因为后⾯还有(3)需要执⾏。
那么反过来,从最后⼀个参数,从右往左看,它的左侧必然是⼀个函数。以此类推,如果前⾯有n个(),那就是有n个函数返回了结果,只是
返回的结果,还是⼀个函数。是不是有点递归的意思?
⽹上有⼀些不同的柯⾥化的实现⽅式,以下是个⼈觉得最容易理解的写法:
functioncurry(fn,currArgs){
returnfunction(){
letargs=[].(arguments);
//⾸次调⽤时,若未提供最后⼀个参数currArgs,则不⽤进⾏args的拼接
if(currArgs!==undefined){
args=(currArgs);
}
//递归调⽤
if(<){
returncurry(fn,args);
}
//递归出⼝
(null,args);
}
}
解析⼀下curry函数的写法:
⾸先,它有2个参数,fn指的就是本⽂⼀开始的源处理函数sum。currArgs是调⽤curry时传⼊的参数列表,⽐如(1,2)(3)这样的。
再看到curry函数内部,它会整个返回⼀个匿名函数。
再接下来的letargs=[].(arguments);,意思是将arguments数组化。arguments是⼀个类数组的结构,它并不是⼀个真的数
组,所以没法使⽤数组的⽅法。我们⽤了call的⽅法,就能愉快地对args使⽤数组的原⽣⽅法了。在这篇「⼲货」深⼊理解JavaScript
的Apply、Call和Bind⽅法中,有关于call更详细的⽤法介绍。
currArgs!==undefined的判断,是为了解决递归调⽤时的参数拼接。
最后,判断args的个数,是否与fn(也就是sum)的参数个数相等,相等了就可以把参数都传给fn,进⾏输出;否则,继续递归调⽤,
直到两者相等。
测试⼀下:
functionsum(a,b,c){
(a+b+c);
}
constfn=curry(sum);
fn(1,2,3);//6
fn(1,2)(3);//6
fn(1)(2,3);//6
fn(1)(2)(3);//6
都能输出6了,搞定!
柯⾥化的⽤途
理解了柯⾥化的实现之后,让我们来看⼀下它的实际应⽤。柯⾥化的⽬的是,减少代码冗余,以及增加代码的可读性。来看下⾯这个例⼦:
constpersons=[
{name:'kevin',age:4},
{name:'bob',age:5}
];
//这⾥的curry函数,之前已实现
constgetProp=curry(function(obj,index){
constargs=[].(arguments);
returnobj[args[-1]];
});
constages=(getProp('age'));//[4,5]
constnames=(getProp('name'));//['kevin','bob']
在实际的业务中,我们常会遇到类似的列表数据。⽤getProp就可以很⽅便地,取出列表中某个key对应的值。
需要注意的是,constnames=(getProp('name'));执⾏这条语句时getProp的参数只有⼀个name,⽽定义getProp⽅
法时,传⼊curry的参数有2个,obj和index(这⾥必须写2个及以上的参数)。
为什么要这么写?关键就在于arguments的隐式传参。
constgetProp=curry(function(obj,index){
(arguments);
//会输出4个类数组,取其中⼀个来看
//{
//0:{name:"kevin",age:4},
//1:0,
//2:[
//{name:"kevin",age:4},
//{name:"bob",age:5}
//],
//3:"age"
//}
});
map是Array的原⽣⽅法,它的⽤法如下:
varnew_array=(functioncallback(currentValue[,index[,array]]){
//Returnelementfornew_array
}[,thisArg]);
所以,我们传⼊的name,就排在了arguments的最后。为了拿到name对应的值,需要对类数组arguments做点转换,让它可以使⽤
Array的原⽣⽅法。所以,最终getProp⽅法定义成了这样:
constgetProp=curry(function(obj,index){
constargs=[].(arguments);
returnobj[args[-1]];
});
当然,还有另外⼀种写法,curry的实现更好理解,但是调⽤的代码却变多了,⼤家可以根据实际情况进⾏取舍。
constgetProp=curry(function(key,obj){
returnobj[key];
});
constages=(item=>{
returngetProp(item)('age');
});
constnames=(item=>{
returngetProp(item)('name');
});
最后,来看⼀个Memoization的例⼦。它⽤于优化⽐较耗时的计算,通过将计算结果缓存到内存中,这样对于同样的输⼊值,下次只需要
中内存中读取结果。
functionmemoizeFunction(func){
constcache={};
returnfunction(){
letkey=arguments[0];
if(cache[key]){
returncache[key];
}el{
constval=(null,arguments);
cache[key]=val;
returnval;
}
};
}
constfibonacci=memoizeFunction(function(n){
return(n===0||n===1)?n:fibonacci(n-1)+fibonacci(n-2);
});
(fibonacci(100));//输出3542248480
(fibonacci(100));//输出3542248480
代码中,第2次计算fibonacci(100)则只需要在内存中直接读取结果。
附上⾯试题
最近朋友在准备⾯试,它为⼀道编程题所困,向我求助。原题如下:
//写⼀个sum⽅法,当使⽤下⾯的语法调⽤时,能正常⼯作
(sum(2,3));//Outputs5
(sum(2)(3));//Outputs5
这道题要考察的,就是对函数柯⾥化的理解。让我们先来解析⼀下题⽬的要求:
1、如果传递两个参数,我们只需将它们相加并返回。
2、否则,我们假设它是以sum(2)(3)的形式被调⽤的,所以我们返回⼀个匿名函数,它将传递给sum()(在本例中为2)的参数和传递给匿名
函数的参数(在本例中为3)。
所以,sum函数可以这样写:
functionsum(x){
if(==2){
returnarguments[0]+arguments[1];
}
returnfunction(y){
returnx+y;
}
}
arguments的⽤法挺灵活的,在这⾥它则⽤于分割两种不同的情况。当参数只有⼀个的时候,进⾏柯⾥化的处理。
虽然⼀开始理解起来有点云⾥雾⾥的,但⼀旦理解了其中的含义和具体的使⽤场景,⽤起来就会得⼼应⼿了。
函数的柯⾥化,是Javascript中函数式编程的⼀个重要概念。它返回的,是⼀个函数的函数。其实现⽅式,需要依赖参数以及递归,通过
拆分参数的⽅式,来调⽤⼀个多参数的函数⽅法,以达到减少代码冗余,增加可读性的⽬的。
本文发布于:2022-12-27 20:22:15,感谢您对本站的认可!
本文链接:http://www.wtabcd.cn/fanwen/fan/90/42431.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |