码迷,mamicode.com
首页 > 其他好文 > 详细

柯里化(currying)真正作用

时间:2015-10-13 18:42:55      阅读:9536      评论:0      收藏:0      [点我收藏+]

标签:

关于柯里化(currying)的目的和作用一直很模糊,网上的资料很多都没有说到重点,大部分只关注于如何实现柯里化,而对其用途闪烁其词,直到最近看了这里:

http://www.cnblogs.com/pigtail/p/3447660.html 

个人认为所有写代码的技巧目的只有两个:一是提高程序性能,而是使代码模块化,减少耦合增强其可维护性。柯里化的作用很明显是属于第二种。经过柯里化之后,函数的通用性有所降低,但是适用性有所提高。

柯里化并没有我们想象中那么高深,其实我们经常在不知不觉中使用了柯里化,只是你没有发觉而已,让我们举个例子来看柯里化是的用途和如何演变为通用形式的。(关于柯里化的定义请自行百度或者查看我上面给出的链接)

柯里化的演变

假如现在我们基于某个sdk开发一个app,需要调用该sdk提供的api,这些api都有3个参数,分别是调用你的app的id,用户的id和用户的nickname(这个场景在hybrid式的webApp开发很常见)

api1( "myApp", "xiaoming", "小明");
api1( "myApp", "xiaohong", "小红");
api1( "myApp", "xiaogang", "小刚");

 

这时候你可能发现了一些端倪,代码中出现了重复的部分(你的app的id),这时候重构代码把公共的部分抽取出来,你可能会这么封装

let myApi1 = function(uid, nickname){
    api1( "myApp", uid, nickname);
}
myApi1("xiaoming", "小明");
myApi1("xiaohong", "小红");
myApi1("xiaogang", "小刚");

 

然后你继续开发,发现要调用另外一个api,这个api跟上面那个很相似,只是第2、3个参数变为了用户的令牌和用户id。因为你的app的id是不变的,有了之前的经验,你或许会这么封装。

let myApi2 = function(token, uid){
    api2( "myApp", token, uid);
}
myApi2("token1", "xiaoming");
myApi2("token2", "xiaohong");
myApi2("token3", "xiaogang");

 

继续开发的时候,你很有可能会继续调用类似的api3,api4,api5......,这些api的第一个参数都是你的app的id,而在你的某个项目里面,这都是固定不变,那么你的程序里面会出现类似的封装,可以看出又有了重复的代码

let myApi3 = function(p1, p2){
    api3( "myApp", p1, p2);
}
let myApi4 = function(p1, p2){
    api4( "myApp", p1, p2);
}
let myApi5 = function(p1, p2){
    api5( "myApp", p1, p2);
}
....

 

此时你或许会这么封装去减少重复

let getMyApi = function(api){
    return function(p1, p2){
        api("myApp", p1, p2)
    }    
}

let myApi1 = getMyApi(api1)
let myApi2 = getMyApi(api2)
let myApi3 = getMyApi(api3)

 

一切看起来很好,但是很快又碰到了其它问题,你碰到了另外一种类型的api,它也是3个参数,第1个参数是app的id,第2个参数是你的app的名字,两个参数都是固定不变的,那么根据上面封装经验,你可能会这么封装:

let getMyApi2 = function(api){
    return function(p2){
        api("myApp", "myAppName", p2)
    }    
}

let myApi4 = getMyApi2(api4)
let myApi5 = getMyApi2(api5)
let myApi6 = getMyApi2(api6)

这时你会发现 getMyApi 和 getMyApi2 似乎有一些相似的地方,它们都是:
1,将需要调用的api(一个函数)作为第1个参数传入
2,然后固定住部分不变的参数
3,然后返回新的函数

既然有相似的地方,说明还可以做进一步的抽象,我们可以这么写
let generalGetMyApi = function(api) {
    //把函数和需要固定住的参数都传递进来
    let settledArgs = [].splice.call(arguments, 1);

    //返回一个新的函数,这个函数包含了已经固定住部分参数,并且传入一些易变的参数
    return function() {

        //将不变的参数和易变的参数重新传进这个函数再执行
        let mutableArguments = arguments;
         api.call(null, [].concat.apply(settledArgs, mutableArguments));
    }
}

以上就是一个简单的通用的柯里化的做法,有了这种做法,上面我们重构的问题都非常好解决了

当遇到第一种类型的api(第1参数需要固定),那么可以这样做

let myApi1 = generalGetMyApi(api1, "myApp1");
myApi1("xiaoming", "小明");
myApi1("xiaohong", "小红");
myApi1("xiaogang", "小刚");

let myApi12 = generalGetMyApi(api2, "myApp1");
myApi2("token1", "xiaoming");
myApi2("token2", "xiaohong");
myApi2("token3", "xiaogang")

 

当遇到第二种类型的api(第1、2参数需要固定),那么可以这样做

let myApi3 = generalGetMyApi(api3, "myApp1","nameOfApp1"); 
myApi2("other args1")
myApi2("other args2")
myApi2("other args3")

 

现在代码没有了重复的味道,并且函数generalGetMyApi还可以在其它项目中复用。这就是柯里化的目的,将函数的通用性降低,提高了函数的适用性,而这一些是通过缓存部分参数实现的。

 

 

 



 











 

柯里化(currying)真正作用

标签:

原文地址:http://www.cnblogs.com/youth7/p/4871283.html

(2)
(7)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!