摘要:提到函數式編程,就不得不提柯里化和組合。說實話,在之前的項目開發中,對柯里化和組合的運用不是太多,因為不太清楚應該在哪些情況下應該使用它們。所以在這篇文章中,我們將詳細的介紹柯里化和組合的用法以及使用場景。
提到函數式編程,就不得不提柯里化和組合。說實話,在之前的項目開發中,對柯里化和組合的運用不是太多,因為不太清楚應該在哪些情況下應該使用它們。所以在這篇文章中,我們將詳細的介紹柯里化和組合的用法以及使用場景。
柯里化 Curry首先說說什么是柯里化, 簡單來講就是部分應用, 也就是說 只傳遞函數的一部分參數來調用它,讓它返回一個函數去處理剩下的參數。
參數復用先來看個例子,創建一個 say 函數,打印出帶有名字,前綴和問候語的一句話。
const say = (name, prefix, greeting) => `${greeting}, ${prefix} ${name}!`; say("Tom", "Mr", "Hello"); // "Hello, Mr Tom" say("James", "Mr", "Hello"); // "Hello, Mr James"
在上面的例子中,我們每一次調用 say 函數都必須傳入完整的三個參數,才能保證正確的運行結果,否則,雖然程序還是會正常運行,可是未傳入的部分會變成 undefined。
利用柯里化,我們可以固定住其中的部分參數,在調用的時候,這個參數就相當于已經被記住了,不需要再次傳遞,也就是我們這里說的參數復用。
const say = prefix => greeting => name => `${greeting}, ${prefix} ${name}!`; const sayToMr = say("Mr"); const sayToMiss = say("Miss"); const greetMr = sayToMr("Hello"); const greetMiss = sayToMiss("Hi"); greetMr("Tom"); // "Hi, Miss Cindy!" greetMiss("Cindy"); // "Hello, Mr Tom!"
這時候如果我們想輸入相同的問候語 Hello, 我們發現,原來的結構好像不太滿足了呃,于是我們開始調整參數的位置。
const say = greeting => prefix => name => `${greeting}, ${prefix} ${name}!`; const greet = say("Hello"); const greetMeiNv = greet("美女"); const greetShuaiGe = greet("帥哥"); greetShuaiGe("Tom"); // "Hello, 帥哥 Tom!" greetMeiNv("Cindy"); // "Hello, 美女 Cindy!"
Note: 在使用柯里化的時候,參數的順序很重要,可以考慮根據易變化的程度來排列參數,把不容易變化的參數通過柯里化固定起來,將需要處理的參數放到最后一位。延遲執行
在上面的例子中,通過柯里化,我們居然多造出了 3 個函數!簡直就是函數工廠嘛!但是猛地一想,如果如果是 100 個參數呢,難道要寫一百次?有沒有一種方法可以簡單的幫我們實現柯里化?
我要開始放書上的代碼了。
function curry(fn) { var outerArgs = Array.prototype.slice.call(arguments, 1); return function() { var innerArgs = Array.prototype.slice.call(arguments), finalArgs = outerArgs.concat(innerArgs); return fn.apply(null, finalArgs); }; } const say = (name, prefix, greeting) => `${greeting}, ${prefix} ${name}!`; const curriedSay = curry(say); curriedSay("Tom", "Mr", "Hello"); // "Hello, Mr Tom!" curry(say,"Tom", "Mr")("Hello"); // "Hello, Mr Tom!"
簡單解釋一下上面的代碼,首先是得到除了第一個參數 fn 之外的所有的外部傳參 outerArgs,這里的 arguments 是一個長得像數組的對象,所以我們要使用 Array.proptype.slice 將其轉變成真正的數組。 innerArgs 用來獲取調用這個匿名函數時的傳參。最后將外部傳參 outerArgs 和內部傳參 innerArgs 合并,調用 fn。也就是說這時 fn 才被調用。
就好比刷信用卡和儲蓄卡,刷儲蓄卡就是把你的錢馬上轉到別人口袋,刷信用卡是銀行先幫你墊著,到下個月再把錢還給銀行。總之,最后都是花自己的錢。不過這樣有一個好處就是,就是可以讓你養成拆分函數,并給函數良好命名的習慣,以及更好的處理和抽象代碼的邏輯。
使用 Ramda / Lodash 生成柯里化函數當然,你也可以可以使用 lodash 或者 ramda 這樣的庫來快速柯里化你的函數,這樣可以省去很多重復造輪子的工作。
下面以使用 lodash 為例。
const say = (prefix, name, greeting) => `${greeting}, ${prefix} ${name}!`; const curreiedSay = _.curry(say); curreiedSay("Mr","Tom","Hello"); // "Hello, Mr Tom!" curreiedSay("Mr")("Tom","Hello"); // "Hello, Mr Tom!" curreiedSay("Mr")("Tom")("Hello"); // "Hello, Mr Tom!" curreiedSay("Tom")(_,"Hello")("Mr"); // "Hello, Mr Tom!"
lodash 和 Ramda 都提供了一系列柯里化函數的包裝方法,感興趣的同學可以打開 lodash / ramda 官網,在 console 里面試一下。組合 Compose
組合,顧名思義,也就是把多個函數組合起來變成一個函數。
const compose = (fn1, fn2) => args => fn1(fn2(args)); const toUpperCase = value => value.toUpperCase(); const addSuffix = value => `${value} is good!`; const format = compose(toUpperCase, addSuffix); format("apple"); // "APPLE IS GOOD!"
上面的例子中,fn2 先執行,然后將返回值作為 fn1 的參數,所以 compose 里面的方法是從右向左執行的。就像一條流水線一樣,a 流水線先把汽車組裝好,然后交給 b 流水線進行噴漆,再交給 c 流水線打磨等等,最后得到一輛嶄新的汽車。
結合柯里化和組合 Curry + Compose學習完柯里化和組合之后,讓我們將它們結合起來使用,一定能夠碰撞出更強的火花,產生更大的威力。
說寫就寫。
假設有一個數組,我們期望先對數組進行去重,然后對數組進行求和或求積。
const unique = arr => _.uniq(arr); // 數組去重 const sum = arr => _.reduce(arr, (total, n) => total + n); // 數組元素的累加之和 const multiply = arr => _.reduce(arr, (total, n) => total * n); // 數組元素的乘積 const getTotal = fn => arr => _.flowRight(fn, unique)(arr); // 從右至左, 先去重, 再執行 fn const arr1 = [1, 2, 3, 4, 4, 5, 5]; const arr2 = [1, 2, 2, 3, 4, 4, 5]; const getSumTotal = getTotal(sum); // 通過柯里化產生一個新的函數 const getMultiplyTotal = getTotal(multiply); // 通過柯里化產生一個新的函數 getSumTotal(arr1); // 15 getMultiplyTotal(arr2); // 120
現在的前端社區中,函數式編程隨處可見,柯里化和組合也成為了我們必須掌握的技能。在項目開發中,可以不斷的去加強練習。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/91134.html
摘要:函數式編程,一看這個詞,簡直就是學院派的典范。所以這期周刊,我們就重點引入的函數式編程,淺入淺出,一窺函數式編程的思想,可能讓你對編程語言的理解更加融會貫通一些。但從根本上來說,函數式編程就是關于如使用通用的可復用函數進行組合編程。 showImg(https://segmentfault.com/img/bVGQuc); 函數式編程(Functional Programming),一...
摘要:組合的概念是非常直觀的,并不是函數式編程獨有的,在我們生活中或者前端開發中處處可見。其實我們函數式編程里面的組合也是類似,函數組合就是一種將已被分解的簡單任務組織成復雜的整體過程。在函數式編程的世界中,有這樣一種很流行的編程風格。 JavaScript函數式編程,真香之認識函數式編程(一) 該系列文章不是針對前端新手,需要有一定的編程經驗,而且了解 JavaScript 里面作用域,閉...
摘要:函數式編程逐漸被邊緣化,被拋棄到學術界和非主流的場外。組合式編程的重新崛起年左右,有個巨大的變化爆發了。人們開始逐漸在私下里談論函數式編程。箭頭函數對于函數式編程的爆發起到了推動劑的作用。現在很少看到那種不用函數式編程的大型應用了。 showImg(https://segmentfault.com/img/remote/1460000009036867?w=800&h=364); 本...
摘要:本書主要探索函數式編程的核心思想。我們在中應用的僅僅是一套基本的函數式編程概念的子集。我稱之為輕量級函數式編程。通常來說,關于函數式編程的書籍都熱衷于拓展閱讀者的知識面,并企圖覆蓋更多的知識點。,本書統稱為函數式編程者。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson - 《You-Dont-Know-JS》作者 譯者團隊(排名不分先后)...
摘要:但是,對函數式編程而言,這個行為的重要性是毋庸置疑的。關于該模式更正式的說法是偏函數嚴格來講是一個減少函數參數個數的過程這里的參數個數指的是希望傳入的形參的數量。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 關于譯者:這是一個流淌著滬江血液的純粹工程:認真,是 HTML 最堅實的梁柱;分享,是...
閱讀 1509·2021-08-09 13:47
閱讀 2769·2019-08-30 15:55
閱讀 3492·2019-08-29 15:42
閱讀 1115·2019-08-29 13:45
閱讀 3009·2019-08-29 12:33
閱讀 1742·2019-08-26 11:58
閱讀 983·2019-08-26 10:19
閱讀 2411·2019-08-23 18:00