javascript高階函數(shù)應(yīng)用——柯里化

高階函數(shù):
在數(shù)學(xué)和計(jì)算機(jī)科學(xué)中,高階函數(shù)是至少滿足下列一個(gè)條件的函數(shù):
1.接受一個(gè)或多個(gè)函數(shù)作為輸入:forEach sort map filter reduce
2.輸出一個(gè)函數(shù):lodash.curry
不過(guò)它也可以同時(shí)滿足兩個(gè)條件:Function.prototype.bind

這里對(duì)高階函數(shù)不過(guò)多講解,這節(jié)我們來(lái)學(xué)習(xí)柯里化。

在計(jì)算機(jī)科學(xué)中,柯里化(Currying)是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)且返回結(jié)果的新函數(shù)的技術(shù) 。在直覺(jué)上,柯里化聲稱“如果你固定某些參數(shù),你將得到接受余下參數(shù)的一個(gè)函數(shù)”。所以對(duì)于有兩個(gè)變量的函數(shù)yx,如果固定了 y = 2,則得到有一個(gè)變量的函數(shù) 2x——柯里化·百度百科

由以上,我們來(lái)一段代碼

// 假設(shè)我們要寫(xiě)一個(gè)+1的函數(shù)
function sum(x, y){
    return x+y
}
//此時(shí),這個(gè)sum就稱為對(duì)于x和y的函數(shù)
sum(1, 2)  // 3
//由柯里化的概念,我們可以固定一個(gè)參數(shù),使后續(xù)操作可以只傳一個(gè)參數(shù)
function addOne(y){
  return 1+y
}
addOne(2) //3
addOne(7) //8

//可如果,又有一個(gè)+2的需求呢,那好,我再寫(xiě)一遍
function addTwo(a){
  return a+2
}
addTwo(3) //5

//那再有+3,+4的需求呢? 

這時(shí),我們就可以使用柯里化來(lái)簡(jiǎn)便這個(gè)過(guò)程,柯里化是惰性求值的,即在你需要的到值的時(shí)候,再取值。
這里借鑒一下《JavaScript設(shè)計(jì)模式與開(kāi)發(fā)實(shí)踐》書(shū)中的例子

假設(shè)我們要編寫(xiě)一個(gè)計(jì)算每月開(kāi)銷的函數(shù)。在每天結(jié)束之前,我們都要記錄今天花掉了多少錢(qián)。代碼如下:

var monthlyCost = 0; 
var cost = function( money ){ 
 monthlyCost += money; 
}; 
cost( 100 ); // 第 1 天開(kāi)銷
cost( 200 ); // 第 2 天開(kāi)銷
cost( 300 ); // 第 3 天開(kāi)銷
//cost( 700 ); // 第 30 天開(kāi)銷
alert ( monthlyCost ); // 輸出:600

通過(guò)這段代碼可以看到,每天結(jié)束后我們都會(huì)記錄并計(jì)算到今天為止花掉的錢(qián)。但我們其實(shí)并不太關(guān)心每天花掉了多少錢(qián),而只想知道到月底的時(shí)候會(huì)花掉多少錢(qián)。也就是說(shuō),實(shí)際上只需要在月底計(jì)算一次。如果在每個(gè)月的前 29 天,我們都只是保存好當(dāng)天的開(kāi)銷,直到第 30 天才進(jìn)行求值計(jì)算,這
樣就達(dá)到了我們的要求。

以上需求,我們就可以利用柯里化的思想來(lái)惰性求值,先將一部分參數(shù)存起來(lái),當(dāng)我們實(shí)際需要值的時(shí)候再來(lái)求值,雖然下面的 cost 函數(shù)還不是一個(gè) currying 函數(shù)的完整實(shí)現(xiàn),但有助于 我們了解其思想。

var cost = (function(){ 
    var args = []; 
    return function(){ 
        if ( arguments.length === 0 ){ 
            var money = 0; 
            for ( var i = 0, l = args.length; i < l; i++ ){ 
                money += args[ i ];
            }
            return money;
        }else{
             [].push.apply(args, arguments)
            //或者 args = args.concat([].slice.call(arguments))
        }
    }
})()
cost(100)
cost(253)
cost()  // 353

接下來(lái),我們要寫(xiě)一個(gè)正式的curry函數(shù)

// 第一個(gè)參數(shù)是我們需要柯里化的原函數(shù),同時(shí),在它后邊你還可以傳入一些初始的值。
//這里,你可以注明形參(只傳fn的話),因?yàn)榭紤]到初始值的問(wèn)題,我們就以arguments來(lái)代替了
var currying = function( fn ){ // 參數(shù)為原函數(shù)
       var args = []; 
       return function(){   // 柯里化后的新函數(shù)
           if ( arguments.length === 0 ){  // 參數(shù)數(shù)數(shù)量為0時(shí),意為求值
               return fn.apply( this, args ); 
           }else{     // 否則,將這些值保存起來(lái)
               [].push.apply( args, arguments ); 
               return arguments.callee; 
           } 
       } 
};
var cost = (function(){ 
       var money = 0; 
       return function(){ 
           for ( var i = 0, l = arguments.length; i < l; i++ ){ 
               money += arguments[ i ]; 
           } 
           return money; 
       } 
})();
var cost = currying( cost ); // 轉(zhuǎn)化成 currying 函數(shù)
cost( 100 ); // 未真正求值
cost( 200 ); // 未真正求值
cost( 300 ); // 未真正求值
alert ( cost() ); // 求值并輸出:600

至此,我們就完成了對(duì)柯里化的初步了解

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容