日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一文讲懂什么是函数柯里化,柯里化的目的及其代码实现

發布時間:2024/1/18 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一文讲懂什么是函数柯里化,柯里化的目的及其代码实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

柯里化(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?庫的?_.curryalert(?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

所以:

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

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

  • 高級柯里化實現

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

    它非常短:

    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 執行分支:

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

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

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

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

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

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

  • 包裝器 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

    看完三件事

    如果你覺得本文對你有幫助,我想請你幫個忙:

  • 轉發本文點贊或者點個「在看」,是對我最大的認可和支持;

  • 關注公眾號「技術漫談」,訂閱更多精彩內容,獲取更多學習資料;

  • 公眾號后臺回復「加群」,加入算法和技術交流群,與更多讀者交流。


  • ?

    總結

    以上是生活随笔為你收集整理的一文讲懂什么是函数柯里化,柯里化的目的及其代码实现的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。