溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

什么是函數柯里化

發布時間:2021-10-23 10:26:22 來源:億速云 閱讀:141 作者:iii 欄目:web開發

本篇內容介紹了“什么是函數柯里化”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

柯里化(Currying)

柯里化(Currying)[1]是一種關于函數的高階技術。它不僅被用于 JavaScript,還被用于其他編程語言。

柯里化是一種函數的轉換,它是指將一個函數從可調用的 f(a, b, c) 轉換為可調用的 f(a)(b)(c)。

柯里化不會調用函數。它只是對函數進行轉換。

讓我們先來看一個例子,以更好地理解我們正在講的內容,然后再進行一個實際應用。

我們將創建一個輔助函數 curry(f),該函數將對兩個參數的函數 f 執行柯里化。換句話說,對于兩個參數的函數 f(a, b) 執行 curry(f)  會將其轉換為以 f(a)(b) 形式運行的函數:

function curry(f) { // curry(f) 執行柯里化轉換   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

正如你所看到的,實現非常簡單:只有兩個包裝器(wrapper)。

  • curry(func) 的結果就是一個包裝器 function(a)。

  • 當它被像 curriedSum(1) 這樣調用時,它的參數會被保存在詞法環境中,然后返回一個新的包裝器 function(b)。

  • 然后這個包裝器被以 2 為參數調用,并且,它將該調用傳遞給原始的 sum 函數。

柯里化更高級的實現,例如 lodash 庫的  _.curry[2],會返回一個包裝器,該包裝器允許函數被正常調用或者以偏函數(partial)的方式調用:

function sum(a, b) {   return a + b; }  let curriedSum = _.curry(sum); // 使用來自 lodash 庫的 _.curry  alert( curriedSum(1, 2) ); // 3,仍可正常調用 alert( curriedSum(1)(2) ); // 3,以偏函數的方式調用

柯里化?目的是什么?

要了解它的好處,我們需要一個實際中的例子。

例如,我們有一個用于格式化和輸出信息的日志(logging)函數 log(date, importance,  message)。在實際項目中,此類函數具有很多有用的功能,例如通過網絡發送日志(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)

現在,我們可以輕松地為當前日志創建便捷函數:

// logNow 會是帶有固定第一個參數的日志的偏函數 let logNow = log(new Date());  // 使用它 logNow("INFO", "message"); // [HH:mm] INFO message

現在,logNow 是具有固定第一個參數的 log,換句話說,就是更簡短的“偏應用函數(partially applied  function)”或“偏函數(partial)”。

我們可以更進一步,為當前的調試日志(debug log)提供便捷函數:

let debugNow = logNow("DEBUG");  debugNow("message"); // [HH:mm] DEBUG message

所以:

  1. 柯里化之后,我們沒有丟失任何東西:log 依然可以被正常調用。

  2. 我們可以輕松地生成偏函數,例如用于生成今天的日志的偏函數。

高級柯里化實現

如果你想了解更多細節,下面是用于多參數函數的“高級”柯里化實現,我們也可以把它用于上面的示例。

它非常短:

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,仍然可以被正常調用 alert( curriedSum(1)(2,3) ); // 6,對第一個參數的柯里化 alert( curriedSum(1)(2)(3) ); // 6,全柯里化

新的 curry 可能看上去有點復雜,但是它很容易理解。

curry(func) 調用的結果是如下所示的包裝器 curried:

// func 是要轉換的函數 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));     }   } };

當我們運行它時,這里有兩個 if 執行分支:

  1. 現在調用:如果傳入的 args 長度與原始函數所定義的(func.length)相同或者更長,那么只需要將調用傳遞給它即可。

  2. 獲取一個偏函數:否則,func 還沒有被調用。取而代之的是,返回另一個包裝器pass,它將重新應用  curried,將之前傳入的參數與新的參數一起傳入。然后,在一個新的調用中,再次,我們將獲得一個新的偏函數(如果參數不足的話),或者最終的結果。

例如,讓我們看看 sum(a, b, c) 這個例子。它有三個參數,所以 sum.length = 3。

對于調用 curried(1)(2)(3):

  1. 第一個調用 curried(1) 將 1 保存在詞法環境中,然后返回一個包裝器 pass。

  2. 包裝器 pass 被調用,參數為 (2):它會獲取之前的參數 (1),將它與得到的 (2) 連在一起,并一起調用 curried(1,  2)。由于參數數量仍小于 3,curry 函數依然會返回 pass。

  3. 包裝器 pass 再次被調用,參數為 (3),在接下來的調用中,pass(3) 會獲取之前的參數 (1, 2) 并將 3 與之合并,執行調用  curried(1, 2, 3) — 最終有 3 個參數,它們被傳入最原始的函數中。

如果這還不夠清楚,那你可以把函數調用順序在你的腦海中或者在紙上過一遍。

只允許確定參數長度的函數

柯里化要求函數具有固定數量的參數。

使用 rest 參數的函數,例如 f(...args),不能以這種方式進行柯里化。

比柯里化多一點

根據定義,柯里化應該將 sum(a, b, c) 轉換為 sum(a)(b)(c)。

但是,如前所述,JavaScript 中大多數的柯里化實現都是高級版的:它們使得函數可以被多參數變體調用。

“什么是函數柯里化”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女