您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“什么是函數(shù)柯里化”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
柯里化(Currying)
柯里化(Currying)[1]是一種關(guān)于函數(shù)的高階技術(shù)。它不僅被用于 JavaScript,還被用于其他編程語言。
柯里化是一種函數(shù)的轉(zhuǎn)換,它是指將一個函數(shù)從可調(diào)用的 f(a, b, c) 轉(zhuǎn)換為可調(diào)用的 f(a)(b)(c)。
柯里化不會調(diào)用函數(shù)。它只是對函數(shù)進(jìn)行轉(zhuǎn)換。
讓我們先來看一個例子,以更好地理解我們正在講的內(nèi)容,然后再進(jìn)行一個實際應(yīng)用。
我們將創(chuàng)建一個輔助函數(shù) curry(f),該函數(shù)將對兩個參數(shù)的函數(shù) f 執(zhí)行柯里化。換句話說,對于兩個參數(shù)的函數(shù) f(a, b) 執(zhí)行 curry(f) 會將其轉(zhuǎn)換為以 f(a)(b) 形式運行的函數(shù):
function curry(f) { // curry(f) 執(zhí)行柯里化轉(zhuǎn)換 return function(a) { return function(b) { return f(a, b); }; }; } // 用法 function sum(a, b) { return a + b; } let curriedSum = curry(sum); alert( curriedSum(1)(2) ); // 3
正如你所看到的,實現(xiàn)非常簡單:只有兩個包裝器(wrapper)。
curry(func) 的結(jié)果就是一個包裝器 function(a)。
當(dāng)它被像 curriedSum(1) 這樣調(diào)用時,它的參數(shù)會被保存在詞法環(huán)境中,然后返回一個新的包裝器 function(b)。
然后這個包裝器被以 2 為參數(shù)調(diào)用,并且,它將該調(diào)用傳遞給原始的 sum 函數(shù)。
柯里化更高級的實現(xiàn),例如 lodash 庫的 _.curry[2],會返回一個包裝器,該包裝器允許函數(shù)被正常調(diào)用或者以偏函數(shù)(partial)的方式調(diào)用:
function sum(a, b) { return a + b; } let curriedSum = _.curry(sum); // 使用來自 lodash 庫的 _.curry alert( curriedSum(1, 2) ); // 3,仍可正常調(diào)用 alert( curriedSum(1)(2) ); // 3,以偏函數(shù)的方式調(diào)用
柯里化?目的是什么?
要了解它的好處,我們需要一個實際中的例子。
例如,我們有一個用于格式化和輸出信息的日志(logging)函數(shù) log(date, importance, message)。在實際項目中,此類函數(shù)具有很多有用的功能,例如通過網(wǎng)絡(luò)發(fā)送日志(log),在這兒我們僅使用 alert:
function log(date, importance, message) { alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`); }
讓我們將它柯里化!
log = _.curry(log);
柯里化之后,log 仍正常運行:
log(new Date(), "DEBUG", "some debug"); // log(a, b, c)
……但是也可以以柯里化形式運行:
log(new Date())("DEBUG")("some debug"); // log(a)(b)(c)
現(xiàn)在,我們可以輕松地為當(dāng)前日志創(chuàng)建便捷函數(shù):
// logNow 會是帶有固定第一個參數(shù)的日志的偏函數(shù) let logNow = log(new Date()); // 使用它 logNow("INFO", "message"); // [HH:mm] INFO message
現(xiàn)在,logNow 是具有固定第一個參數(shù)的 log,換句話說,就是更簡短的“偏應(yīng)用函數(shù)(partially applied function)”或“偏函數(shù)(partial)”。
我們可以更進(jìn)一步,為當(dāng)前的調(diào)試日志(debug log)提供便捷函數(shù):
let debugNow = logNow("DEBUG"); debugNow("message"); // [HH:mm] DEBUG message
所以:
柯里化之后,我們沒有丟失任何東西:log 依然可以被正常調(diào)用。
我們可以輕松地生成偏函數(shù),例如用于生成今天的日志的偏函數(shù)。
高級柯里化實現(xiàn)
如果你想了解更多細(xì)節(jié),下面是用于多參數(shù)函數(shù)的“高級”柯里化實現(xiàn),我們也可以把它用于上面的示例。
它非常短:
function curry(func) { return function curried(...args) { if (args.length >= func.length) { return func.apply(this, args); } else { return function(...args2) { return curried.apply(this, args.concat(args2)); } } }; }
用例:
function sum(a, b, c) { return a + b + c; } let curriedSum = curry(sum); alert( curriedSum(1, 2, 3) ); // 6,仍然可以被正常調(diào)用 alert( curriedSum(1)(2,3) ); // 6,對第一個參數(shù)的柯里化 alert( curriedSum(1)(2)(3) ); // 6,全柯里化
新的 curry 可能看上去有點復(fù)雜,但是它很容易理解。
curry(func) 調(diào)用的結(jié)果是如下所示的包裝器 curried:
// func 是要轉(zhuǎn)換的函數(shù) function curried(...args) { if (args.length >= func.length) { // (1) return func.apply(this, args); } else { return function pass(...args2) { // (2) return curried.apply(this, args.concat(args2)); } } };
當(dāng)我們運行它時,這里有兩個 if 執(zhí)行分支:
現(xiàn)在調(diào)用:如果傳入的 args 長度與原始函數(shù)所定義的(func.length)相同或者更長,那么只需要將調(diào)用傳遞給它即可。
獲取一個偏函數(shù):否則,func 還沒有被調(diào)用。取而代之的是,返回另一個包裝器pass,它將重新應(yīng)用 curried,將之前傳入的參數(shù)與新的參數(shù)一起傳入。然后,在一個新的調(diào)用中,再次,我們將獲得一個新的偏函數(shù)(如果參數(shù)不足的話),或者最終的結(jié)果。
例如,讓我們看看 sum(a, b, c) 這個例子。它有三個參數(shù),所以 sum.length = 3。
對于調(diào)用 curried(1)(2)(3):
第一個調(diào)用 curried(1) 將 1 保存在詞法環(huán)境中,然后返回一個包裝器 pass。
包裝器 pass 被調(diào)用,參數(shù)為 (2):它會獲取之前的參數(shù) (1),將它與得到的 (2) 連在一起,并一起調(diào)用 curried(1, 2)。由于參數(shù)數(shù)量仍小于 3,curry 函數(shù)依然會返回 pass。
包裝器 pass 再次被調(diào)用,參數(shù)為 (3),在接下來的調(diào)用中,pass(3) 會獲取之前的參數(shù) (1, 2) 并將 3 與之合并,執(zhí)行調(diào)用 curried(1, 2, 3) — 最終有 3 個參數(shù),它們被傳入最原始的函數(shù)中。
如果這還不夠清楚,那你可以把函數(shù)調(diào)用順序在你的腦海中或者在紙上過一遍。
只允許確定參數(shù)長度的函數(shù)
柯里化要求函數(shù)具有固定數(shù)量的參數(shù)。
使用 rest 參數(shù)的函數(shù),例如 f(...args),不能以這種方式進(jìn)行柯里化。
比柯里化多一點
根據(jù)定義,柯里化應(yīng)該將 sum(a, b, c) 轉(zhuǎn)換為 sum(a)(b)(c)。
但是,如前所述,JavaScript 中大多數(shù)的柯里化實現(xiàn)都是高級版的:它們使得函數(shù)可以被多參數(shù)變體調(diào)用。
“什么是函數(shù)柯里化”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。