成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一文講懂什么是函數柯里化,柯里化的目的及其代碼實現

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

[[355444]]

柯里化(Currying)

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

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

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

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

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

  1. function curry(f) { // curry(f) 執行柯里化轉換 
  2.   return function(a) { 
  3.     return function(b) { 
  4.       return f(a, b); 
  5.     }; 
  6.   }; 
  7.  
  8. // 用法 
  9. function sum(a, b) { 
  10.   return a + b; 
  11.  
  12. let curriedSum = curry(sum); 
  13.  
  14. alert( curriedSum(1)(2) ); // 3 

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

  • curry(func) 的結果就是一個包裝器 function(a)。
  • 當它被像 curriedSum(1) 這樣調用時,它的參數會被保存在詞法環境中,然后返回一個新的包裝器 function(b)。
  • 然后這個包裝器被以 2 為參數調用,并且,它將該調用傳遞給原始的 sum 函數。

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

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

柯里化?目的是什么?

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

例如,我們有一個用于格式化和輸出信息的日志(logging)函數 log(date, importance, message)。在實際項目中,此類函數具有很多有用的功能,例如通過網絡發送日志(log),在這兒我們僅使用 alert:

  1. function log(date, importance, message) { 
  2.   alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`); 

讓我們將它柯里化!

  1. log = _.curry(log); 

柯里化之后,log 仍正常運行:

  1. log(new Date(), "DEBUG""some debug"); // log(a, b, c) 

……但是也可以以柯里化形式運行:

  1. log(new Date())("DEBUG")("some debug"); // log(a)(b)(c) 

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

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

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

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

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

所以:

  1. 柯里化之后,我們沒有丟失任何東西:log 依然可以被正常調用。
  2. 我們可以輕松地生成偏函數,例如用于生成今天的日志的偏函數。

高級柯里化實現

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

它非常短:

  1. function curry(func) { 
  2.  
  3.   return function curried(...args) { 
  4.     if (args.length >= func.length) { 
  5.       return func.apply(this, args); 
  6.     } else { 
  7.       return function(...args2) { 
  8.         return curried.apply(this, args.concat(args2)); 
  9.       } 
  10.     } 
  11.   }; 
  12.  

用例:

  1. function sum(a, b, c) { 
  2.   return a + b + c; 
  3.  
  4. let curriedSum = curry(sum); 
  5.  
  6. alert( curriedSum(1, 2, 3) ); // 6,仍然可以被正常調用 
  7. alert( curriedSum(1)(2,3) ); // 6,對第一個參數的柯里化 
  8. alert( curriedSum(1)(2)(3) ); // 6,全柯里化 

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

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

  1. // func 是要轉換的函數 
  2. function curried(...args) { 
  3.   if (args.length >= func.length) { // (1) 
  4.     return func.apply(this, args); 
  5.   } else { 
  6.     return function pass(...args2) { // (2) 
  7.       return curried.apply(this, args.concat(args2)); 
  8.     } 
  9.   } 
  10. }; 

當我們運行它時,這里有兩個 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 中大多數的柯里化實現都是高級版的:它們使得函數可以被多參數變體調用。

總結

柯里化 是一種轉換,將 f(a,b,c) 轉換為可以被以 f(a)(b)(c) 的形式進行調用。JavaScript 實現通常都保持該函數可以被正常調用,并且如果參數數量不足,則返回偏函數。

柯里化讓我們能夠更容易地獲取偏函數。就像我們在日志記錄示例中看到的那樣,普通函數log(date, importance, message) 在被柯里化之后,當我們調用它的時候傳入一個參數(如log(date))或兩個參數(log(date, importance))時,它會返回偏函數。

現代 JavaScript 教程:開源的現代 JavaScript 從入門到進階的優質教程。React 官方文檔推薦,與 MDN 并列的 JavaScript 學習教程[3]。

在線免費閱讀:https://zh.javascript.info

參考資料

[1]柯里化(Currying): https://en.wikipedia.org/wiki/Currying

[2]_.curry: https://lodash.com/docs#curry

[3]React 官方文檔推薦,與 MDN 并列的 JavaScript 學習教程: https://zh-hans.reactjs.org/docs/getting-started.html#javascript-resources

本文轉載自微信公眾號「技術漫談」,可以通過以下二維碼關注。轉載本文請聯系技術漫談公眾號。

 

責任編輯:武曉燕 來源: 技術漫談
相關推薦

2021-09-28 07:12:10

avaScriptCurrying柯里化

2017-12-11 15:02:46

Javascript函數式編程currying

2023-08-02 08:01:14

柯里化反柯里化

2025-01-27 00:30:29

柯里化JavaScript函數

2020-09-23 16:07:52

JavaScript函數柯里化

2024-02-29 14:27:37

人工智能機器學習物聯網

2021-06-08 11:36:07

服務微服務框架

2021-09-03 05:03:58

模塊命令項目

2019-12-23 09:31:11

藍云

2020-12-11 11:40:37

RDBAOFRedis

2016-09-22 15:50:38

JavascriptRedux源碼解析

2021-09-27 07:39:52

Go初始化函數package

2023-12-26 07:33:45

Redis持久化COW

2020-07-16 09:02:45

aPaaS云計算aPaaS平臺

2023-04-11 08:01:32

Web 開發源代碼映射

2023-06-28 08:34:02

Bind()函數JavaScript

2019-11-20 10:07:07

Redis數據系統

2020-03-26 09:18:54

高薪本質因素

2021-01-18 13:05:52

Serverless Serverfull FaaS

2010-08-13 15:48:38

Flex模塊化
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩国产在线 | 99日韩| 美女人人操 | 亚洲精品乱码久久久久久按摩 | 精品亚洲一区二区 | 国产亚洲成av人片在线观看桃 | 91久久北条麻妃一区二区三区 | 国产一区二区三区欧美 | 91精品国产综合久久福利软件 | 精品视频在线免费观看 | 污书屋| 成人在线视频网站 | 欧美 日韩 国产 成人 在线 | 欧美日韩亚洲国产综合 | 天天天操 | 国产激情精品视频 | 日韩伦理一区二区 | 999观看免费高清www | 91在线精品秘密一区二区 | 天天操天天射天天 | 国产在线观看一区 | 精品一区av | 国产激情免费视频 | 国产日韩欧美 | 一区二区三区在线 | 欧美一级欧美一级在线播放 | 成人在线视频免费看 | 日韩欧美福利视频 | 一区二区不卡 | 91精品国产综合久久婷婷香蕉 | 国产精品69av| 日日欧美| www.亚洲成人网 | 国产成人综合亚洲欧美94在线 | 午夜久久av | 精品视频一区二区三区在线观看 | 国产精品成人在线 | 一区影院 | 中文字幕视频在线看 | 亚洲欧洲成人av每日更新 | av在线电影网 |