<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          什么是函數(shù)式編程

          共 4338字,需瀏覽 9分鐘

           ·

          2021-03-16 19:53


          作為一名開發(fā)者, 自然是想要寫出優(yōu)雅的, 易于維護(hù)的, 可擴(kuò)展的, 可以預(yù)測的代碼。函數(shù)式編程 (Functional Programming / FP) 的原則能夠很好的命中這些需求。


          函數(shù)式編程是一種編程范式或者說風(fēng)格, 在這種范式下開發(fā)者更關(guān)注不變性, 函數(shù)是一等公民, 引用透明性, 以及純函數(shù)性等性質(zhì). 如果你還不清楚這些名詞那也不用擔(dān)心, 我們將在這篇文章中逐步了解這些術(shù)語.


          函數(shù)式編程從Lambda計(jì)算演變而來, Lambda計(jì)算是一種建立在函數(shù)抽象與函數(shù)推導(dǎo)上的數(shù)學(xué)系統(tǒng). 因此, 大部分函數(shù)式編程語言看起來都十分的"數(shù)學(xué)"(譯者: 比如Haskell, 實(shí)際上JS也滿足函數(shù)式編程的要求). 好消息是, 并不需要通過專門使用函數(shù)式編程語言來引入函數(shù)式編程范式. 在這篇文章中, 我們將使用JavaScript來進(jìn)行演示和示例. JavaScript擁有不少使它能夠滿足函數(shù)式編程要求的同時(shí)又不會(huì)拘泥于此的特性.


          函數(shù)式編程的核心原則


          既然我們已經(jīng)討論了函數(shù)式編程是什么, 現(xiàn)在讓我們來看看函數(shù)式編程背后的核心原則。


          純函數(shù) Pure functions


          我喜歡將函數(shù)比作機(jī)器 - 它們接受一組輸入(參數(shù)), 并且在之后輸出一些東西(返回值). 純函數(shù)沒有"副作用"或者其他與返回值無關(guān)的行為. 一些潛在的副作用包括打印的操作, 比如console.log, 或者對(duì)函數(shù)外的變量進(jìn)行操作之類的。


          這是一個(gè)非純函數(shù)的例子:

          let number = 2;
          function squreaNumber() { number = number * number; // 不純的操作: 對(duì)函數(shù)外部的變量進(jìn)行了修改 consol.log(number); // 不純的操作: 將函數(shù)內(nèi)的操作打印了出來 return number;}


          相對(duì)的, 下面是一個(gè)純函數(shù)的例子, 它接受一個(gè)輸入, 并返回一個(gè)輸出:

          function squreNumber(number) {    return number * number;}squireNumber(2);


          純函數(shù)獨(dú)立于函數(shù)外的狀態(tài)而運(yùn)行, 它們不應(yīng)該依賴于任何自身內(nèi)部以外的變量, 包括全局變量. 在第一個(gè)例子中, 我們使用了在函數(shù)體外部創(chuàng)建的變量 number, 并且在函數(shù)體內(nèi)部對(duì)它進(jìn)行了修改. 這就打破了原則. 如果你深度依賴一個(gè)外部的頻繁發(fā)生變動(dòng)的變量, 你的代碼將會(huì)變得既不可預(yù)測又難以追蹤, 找出bug的位置或者解釋變量的值如何變化將會(huì)變得更加困難. 相反, 使用只有輸入與輸出, 并且變量僅存在函數(shù)內(nèi)部的函數(shù), 將會(huì)使得調(diào)試debug的過程更為簡單.


          此外, 函數(shù)應(yīng)該遵循引用透明性原則, 這意味著, 對(duì)于相同的輸入, 函數(shù)總會(huì)輸出相同的輸出. 在上述的例子中, 如果對(duì)函數(shù)傳入一個(gè)參數(shù) 2 , 那么它將始終返回結(jié)果 4 . 但是對(duì)于一個(gè)產(chǎn)生隨機(jī)數(shù)的函數(shù)來說, 結(jié)果就不是這樣了. 對(duì)于兩次調(diào)用, 給與相同的輸入, 其結(jié)果是不同的.


          // 非引用透明性的Math.random();// 0.1406399143589343Math.random();// 0.26768924082159495


          不可變性 Immutable


          函數(shù)式編程同時(shí)也重視不可變性, 或者說不會(huì)直接修改數(shù)據(jù). 不可變性為函數(shù)的可預(yù)測性提供支持 - 你清楚數(shù)據(jù)的值, 而且它們也不會(huì)被改變, 這將使得代碼變得更加簡單, 也更容易去測試, 并且也更容易在分布式和多線程應(yīng)用中被調(diào)用.


          當(dāng)開始處理數(shù)據(jù)結(jié)構(gòu)時(shí), 不可變性會(huì)頻繁地受到影響. 例如許多JavaScript中的數(shù)組方法都會(huì)直接地改變數(shù)組本身. 比如 .pop() 會(huì)直接移除數(shù)組的最后一個(gè)元素, .splice() 會(huì)將數(shù)組中的一部分移除. 而在函數(shù)式范式中, 我們會(huì)從原數(shù)組中復(fù)制一個(gè)新數(shù)組出來, 并在這個(gè)過程中移除我們想要移除的元素。


          // 直接改變 myArrconst myArr = [1, 2, 3];myArr.pop(); // 3console.log(myArr); // [1, 2];
          // 復(fù)制原數(shù)組, 并且不帶上最后一個(gè)元素const myArr = [1, 2, 3];const myNewArr = myArr.slice(0, 2); // [1, 2]console.log(myArr); // [1, 2, 3]


          函數(shù)是一等公民 First-class functions


          在函數(shù)式編程中, 函數(shù)是一等公民, 這意味著他們能夠被像其他的變量那樣作為值進(jìn)行使用. 我們能夠創(chuàng)建一個(gè)函數(shù)的數(shù)組, 或者將函數(shù)作為參數(shù)傳遞給其他函數(shù), 或者將他們保存在變量中.


          const myFunctionArr = [() => 1 + 2, () => console.log('hi'), x => 3 * x];myFunctionArr[2](2); // 6
          const myFunction = anotherFunction => anotherFunction(20);const secondFunction = x => x * 10;myFunction(secondFunction); // 200


          階函數(shù) Higher-order functions


          高階函數(shù)是指完成這兩個(gè)任務(wù)之一的函數(shù): 使用一個(gè)或多個(gè)函數(shù)作為他的參數(shù); 返回一個(gè)函數(shù). JavaScript內(nèi)建了許多第一類的高階函數(shù), 比如在數(shù)組中常用的 filtermapreduce.


          filter 用來從原數(shù)組中, 對(duì)元素篩選滿足條件的部分后保持順序返回新的數(shù)組。

          const myArr = [1,2,3,4,5];
          const evens = myArr.filter(x => x % 2 === 0); // [2, 4]


           map 用來遍歷整個(gè)數(shù)組, 并且對(duì)每個(gè)元素根據(jù)傳入的邏輯進(jìn)行一個(gè)映射. 在下面這個(gè)例子中, 我們通過給 map 函數(shù)傳入一個(gè)函數(shù)來將每個(gè)元素都乘以2。

          const myArr = [1, 2, 3, 4, 5];
          const doubled = myArr.map(i => i * 2); // [2, 4, 6, 8, 10]


          reduce 根據(jù)輸入的數(shù)組輸出一個(gè)單一的值, 通常用來計(jì)算數(shù)組的元素的值的總和, 或者扁平化數(shù)組, 或者將元素分組.

          const myArr = [1, 2, 3, 4, 5];
          const sum = myArr.reduce((i, runningSum) => i + runningSum); // 15


          建議各位讀者自己實(shí)現(xiàn)一次每個(gè)方法! 舉個(gè)例子, 可以像這樣創(chuàng)建一個(gè) filter 函數(shù):

          const filter = (arr, condition) => {    const filteredArr = [];
          for (let i = 0; i < arr.length; i++) { if (condition(arr[i])) { filteredArr.push(arr[i]); } }
          return filteredArr;}


          第二類高階函返回一個(gè)函數(shù)作為其返回值, 也是一個(gè)相對(duì)常見的范式. 舉個(gè)例子:

          const createGreeting = greeting => persion => `${greeting} ${person}`;
          const sayHi = createGreeting('Hi');console.log(sayHi('Ali')); // 'Hi Ali'
          const sayHello = createGreeting('Hello');console.log(sayHello('Ali')); // 'Hello Ali'

          順帶一提, 函數(shù)的柯里化(Currying)是一個(gè)很類似的技術(shù)。


          函數(shù)組合 Function composition


          將多個(gè)簡單函數(shù)按照一定順序組合成為一個(gè)復(fù)雜函數(shù)的過程被稱為函數(shù)組合. 例如可以將average與sum兩個(gè)函數(shù)組合起來變成一個(gè)averageArray的函數(shù)用來Number數(shù)組的平均值. 每一個(gè)獨(dú)立的function都相對(duì)較小, 并且可以被復(fù)用于其他目的, 而組合后的它們能完成更加完整而獨(dú)立的任務(wù):


          const sum = arr => arr.reduce((i, runningSum) => i + runningSum);const average = (sum, count) => sum/count;
          const averageArray = arr => average(sum(arr), arr.length);


          函數(shù)式編程的好處


          函數(shù)式編程使得代碼更加的模塊化. 開發(fā)者可以使用體量更小的, 可以被一次又一次復(fù)用的函數(shù). 了解每一個(gè)函數(shù)的功能與特性意味著能夠更清晰明了地進(jìn)行調(diào)試與測試. 更不用說這些函數(shù)都是可預(yù)測的.


          此外, 對(duì)于多核的開發(fā), 可以放心地向這些CPU核心分發(fā)函數(shù)的運(yùn)行(譯者: 因?yàn)橹魂P(guān)心輸入和輸出了, 不會(huì)受到外部變量或者狀態(tài)的影響), 繼而能夠達(dá)到更高的運(yùn)行效率.


          怎么樣才能使用函數(shù)式編程?


          開發(fā)者不需要完全地遵守每一個(gè)函數(shù)式編程的規(guī)定. 盡管面向?qū)ο缶幊掏ǔ1灰曌髋c函數(shù)式編程相違背的對(duì)手, 但開發(fā)者仍然可以在使用函數(shù)式編程的一些原則和特性的時(shí)候結(jié)合面向?qū)ο蟮木幊谭妒絹磉M(jìn)行開發(fā).


          舉例來說, React, 吸收了很多函數(shù)編程的原則, 例如不可變的state, 但同時(shí)多年來也保留了基于類的語法.


          函數(shù)式編程幾乎可以通過任何一個(gè)編程語言來實(shí)現(xiàn), 并不需要開發(fā)者去寫Clojure或者Haskell(除非你真的想).


          即使函數(shù)式原則遵循得并不純粹, 函數(shù)式編程仍然能給你的代碼帶來不小的好處.


          瀏覽 58
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  无码激情视频 | 俺也来欧美精品 | 操逼逼AV | www俺来也 | 午夜操大逼 |