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

JavaScript 中的函數(shù)式編程:函數(shù),組合和柯里化

開(kāi)發(fā) 前端
面向?qū)ο缶幊毯秃瘮?shù)式編程是兩種非常不同的編程范式,它們有自己的規(guī)則和優(yōu)缺點(diǎn)。

 [[343484]]

面向?qū)ο缶幊毯秃瘮?shù)式編程是兩種非常不同的編程范式,它們有自己的規(guī)則和優(yōu)缺點(diǎn)。

但是,JavaScript 并沒(méi)有一直遵循一個(gè)規(guī)則,而是正好處于這兩個(gè)規(guī)則的中間,它提供了普通OOP語(yǔ)言的一些方面,比如類、對(duì)象、繼承等等。但與此同時(shí),它還為你提供了函數(shù)編程的一些概念,比如高階函數(shù)以及組合它們的能力。

高階函數(shù)

我們行人人三個(gè)概念中最重要的一個(gè)開(kāi)始:高階函數(shù)。

高階函數(shù)意味著函數(shù)不僅僅是一個(gè)可以從代碼中定義和調(diào)用,實(shí)際上,你可以將它們用作可分配的實(shí)體。如果你使用過(guò)一些JavaScript,那么這并不奇怪。將匿名函數(shù)分配給常量,這樣的事情非常常見(jiàn)。

  1. const adder = (a, b) => { 
  2.   return a + b 

上述邏輯在許多其他語(yǔ)言中是無(wú)效的,能夠像分配整數(shù)一樣分配函數(shù)是一個(gè)非常有用的工具,實(shí)際上,本文涵蓋的大多數(shù)主題都是該函數(shù)的副產(chǎn)品。

高階函數(shù)的好處:封裝行為

有了高階函數(shù),我們不僅可以像上面那樣分配函數(shù),還可以在函數(shù)調(diào)用時(shí)將它們作為參數(shù)傳遞。這為創(chuàng)建一常動(dòng)態(tài)的代碼基打開(kāi)了大門(mén),在這個(gè)代碼基礎(chǔ)上,可以直接將復(fù)雜行為作為參數(shù)傳遞來(lái)重用它。

想象一下,在純面向?qū)ο蟮沫h(huán)境中工作,你想擴(kuò)展類的功能,以完成任務(wù)。在這種情況下,你可能會(huì)使用繼承,方法是將該實(shí)現(xiàn)邏輯封裝在一個(gè)抽象類中,然后將其擴(kuò)展為一組實(shí)現(xiàn)類。這是一種完美的 OOP 行為,并且行之有效,我們:

  • 創(chuàng)建了一個(gè)抽象結(jié)構(gòu)來(lái)封裝我們的可重用邏輯
  • 創(chuàng)建了二級(jí)構(gòu)造
  • 我們重用的原有的類,并擴(kuò)展了它

現(xiàn)在,我們想要的是重用邏輯,我們可以簡(jiǎn)單地將可重用邏輯提取到函數(shù)中,然后將該函數(shù)作為參數(shù)傳遞給任何其他函數(shù),這種方法,可以少省去一些創(chuàng)建“樣板”過(guò)程,因?yàn)椋覀冎皇窃趧?chuàng)建函數(shù)。

下面的代碼顯示了如何在 OOP 中重用程序邏輯。

  1. //Encapsulated behavior封裝行為stract class LogFormatter { 
  2.    
  3.   format(msg) { 
  4.     return Date.now() + "::" + msg 
  5.   }  
  6.  
  7. //重用行為 
  8. class ConsoleLogger extends LogFormatter { 
  9.    
  10.   log(msg) { 
  11.     console.log(this.format(msg)) 
  12.   }   
  13.  
  14. class FileLogger extends LogFormatter { 
  15.  
  16.   log(msg) { 
  17.     writeToFileSync(this.logFile, this.format(msg)) 
  18.   } 

第二個(gè)示是將邏輯提取到函數(shù)中,我們可以混合匹配輕松創(chuàng)建所需的內(nèi)容。你可以繼續(xù)添加更多格式和編寫(xiě)功能,然后只需將它們與一行代碼混合在一起即可:

  1. // 泛型行為抽象 
  2. function format(msg) { 
  3.   return Date.now() + "::" + msg 
  4.  
  5. function consoleWriter(msg) { 
  6.   console.log(msg) 
  7.  
  8. function fileWriter(msg) { 
  9.   let logFile = "logfile.log" 
  10.   writeToFileSync(logFile, msg) 
  11.  
  12. function logger(output, format) { 
  13.   return msg => { 
  14.     output(format(msg)) 
  15.   } 
  16. // 通過(guò)組合函數(shù)來(lái)使用它 
  17. const consoleLogger = logger(consoleWriter, format) 
  18. const fileLogger = logger(fileWriter, format) 

這兩種方法都有優(yōu)點(diǎn),而且都非常有效,沒(méi)有誰(shuí)最優(yōu)。這里只是展示這種方法的靈活性,我們有能力通過(guò) 行為(即函數(shù))作為參數(shù),就好像它們是基本類型(如整數(shù)或字符串)一樣。

高階函數(shù)的好處:簡(jiǎn)潔代碼

對(duì)于這個(gè)好處,一個(gè)很好的例子就是Array方法,例如forEach,map,reduce等等。在非函數(shù)式編程語(yǔ)言(例如C)中,對(duì)數(shù)組元素進(jìn)行迭代并對(duì)其進(jìn)行轉(zhuǎn)換需要使用for循環(huán)或某些其他循環(huán)結(jié)構(gòu)。這就要求我們以指定方式編寫(xiě)代碼,就是需求描述循環(huán)發(fā)生的過(guò)程。

  1. let myArray = [1,2,3,4] 
  2. let transformedArray = [] 
  3.  
  4. for(let i = 0; i < myArray.length; i++) { 
  5.   transformedArray.push(myArray[i] * 2)  

上面的代碼主要做了:

  • 聲明一個(gè)新變量i,該變量將用作myArray的索引,其值的范圍為0到myArray的長(zhǎng)度
  • 對(duì)于i的每個(gè)值,將myArray的值在i的位置相乘,并將其添加到transformedArray數(shù)組中。

這種方法很有效,而且相對(duì)容易理解,然而,這種邏輯的復(fù)雜性會(huì)隨著項(xiàng)目的復(fù)雜程度上升而上升,認(rèn)知負(fù)荷也會(huì)隨之增加。但是,像下面這種方式就更容易閱讀:

  1. const double = x => x * 2; 
  2.  
  3. let myArray = [1,2,3,4]; 
  4. let transformedArray = myArray.map(double); 

與第一種方式相比,這種方式更容易閱讀,而且由于邏輯隱藏在兩個(gè)函數(shù)(map和double)中,因此你不必?fù)?dān)心了解它們的工作原理。你也可以在第一個(gè)示例中將乘法邏輯隱藏在函數(shù)內(nèi)部,但是遍歷邏輯必須存在,這就增加了一些不必要的閱讀阻礙。

柯里化

函數(shù)柯里化是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。我們來(lái)看個(gè)例子:

  1. function adder(a, b) { 
  2.   return a + b 
  3.  
  4. // 變成 
  5. const add10 = x => adder(a, 10) 

現(xiàn)在,如果你要做的就是將10添加到一系列值中,則可以調(diào)用add10而不是每次都使用相同的第二個(gè)參數(shù)調(diào)用adder。這個(gè)事例看起來(lái)比較蠢,但它是體現(xiàn)了 柯里化 的理想。

你可以將柯里化視為函數(shù)式編程的繼承,然后按照這種思路再回到logger的示例,可以得到以下內(nèi)容:

  1. function log(msg, msgPrefix, output) { 
  2.   output(msgPrefix + msg) 
  3. }  
  4.  
  5. function consoleOutput(msg) { 
  6.   console.log(msg) 
  7.  
  8. function fileOutput(msg) { 
  9.   let filename = "mylogs.log" 
  10.   writeFileSync(msg, filename) 
  11.  
  12. const logger = msg => log(msg, ">>", consoleOutput); 
  13. const fileLogger = msg => log(msg, "::", fileOutput); 

log的函數(shù)需要三個(gè)參數(shù),而我們將其引入僅需要一個(gè)參數(shù)的專用版本中,因?yàn)槠渌麅蓚€(gè)參數(shù)已由我們選擇。

注意,這里將log函數(shù)視為抽象類,只是因?yàn)樵谖业氖纠校幌胫苯邮褂盟沁@樣做是沒(méi)有限制的,因?yàn)檫@只是一個(gè)普通的函數(shù)。如果我們使用的是類,則將無(wú)法直接實(shí)例化它。

組合函數(shù)

函數(shù)組合就是組合兩到多個(gè)函數(shù)來(lái)生成一個(gè)新函數(shù)的過(guò)程。將函數(shù)組合在一起,就像將一連串管道扣合在一起,讓數(shù)據(jù)流過(guò)一樣。

在計(jì)算機(jī)科學(xué)中,函數(shù)組合是將簡(jiǎn)單函數(shù)組合成更復(fù)雜函數(shù)的一種行為或機(jī)制。就像數(shù)學(xué)中通常的函數(shù)組成一樣,每個(gè)函數(shù)的結(jié)果作為下一個(gè)函數(shù)的參數(shù)傳遞,而最后一個(gè)函數(shù)的結(jié)果是整個(gè)函數(shù)的結(jié)果。

這是來(lái)自維基百科的函數(shù)組合的定義,粗體部分是比較關(guān)鍵的部分。使用柯里化時(shí),就沒(méi)有該限制,我們可以輕松使用預(yù)設(shè)的函數(shù)參數(shù)。

代碼重用聽(tīng)起來(lái)很棒,但是實(shí)現(xiàn)起來(lái)很難。如果代碼業(yè)務(wù)性過(guò)于具體,就很難重用它。如時(shí)代碼太過(guò)通用簡(jiǎn)單,又很少人使用。所以我們需要平衡兩者,一種制作更小的、可重用的部件的方法,我們可以將其作為構(gòu)建塊來(lái)構(gòu)建更復(fù)雜的功能。

在函數(shù)式編程中,函數(shù)是我們的構(gòu)建塊。每個(gè)函數(shù)都有各自的功能,然后我們把需要的功能(函數(shù))組合起來(lái)完成我們的需求,這種方式有點(diǎn)像樂(lè)高的積木,在編程中我們稱為 組合函數(shù)。

看下以下兩個(gè)函數(shù):

  1. var add10 = function(value) { 
  2.     return value + 10; 
  3. }; 
  4. var mult5 = function(value) { 
  5.     return value * 5; 
  6. }; 

上面寫(xiě)法有點(diǎn)冗長(zhǎng)了,我們用箭頭函數(shù)改寫(xiě)一下:

  1. var add10 = value => value + 10; 
  2. var mult5 = value => value * 5; 

現(xiàn)在我們需要有個(gè)函數(shù)將傳入的參數(shù)先加上 10 ,然后在乘以 5, 如下:

現(xiàn)在我們需要有個(gè)函數(shù)將傳入的參數(shù)先加上 10 ,然后在乘以 5, 如下:

  1. var mult5AfterAdd10 = value => 5 * (value + 10) 

盡管這是一個(gè)非常簡(jiǎn)單的例子,但仍然不想從頭編寫(xiě)這個(gè)函數(shù)。首先,這里可能會(huì)犯一個(gè)錯(cuò)誤,比如忘記括號(hào)。第二,我們已經(jīng)有了一個(gè)加 10 的函數(shù) add10 和一個(gè)乘以 5 的函數(shù) mult5 ,所以這里我們就在寫(xiě)已經(jīng)重復(fù)的代碼了。

使用函數(shù) add10,mult5 來(lái)重構(gòu) mult5AfterAdd10 :

  1. var mult5AfterAdd10 = value => mult5(add10(value)); 

我們只是使用現(xiàn)有的函數(shù)來(lái)創(chuàng)建 mult5AfterAdd10,但是還有更好的方法。

在數(shù)學(xué)中, f ° g 是函數(shù)組合,叫作“f 由 g 組合”,或者更常見(jiàn)的是 “f after g”。因此 (f ° g)(x) 等效于f(g(x)) 表示調(diào)用 g 之后調(diào)用 f。

在我們的例子中,我們有 mult5 ° add10 或 “add10 after mult5”,因此我們的函數(shù)的名稱叫做 mult5AfterAdd10。由于Javascript本身不做函數(shù)組合,看看 Elm 是怎么寫(xiě)的:

  1. add10 value = 
  2.     value + 10 
  3. mult5 value = 
  4.     value * 5 
  5. mult5AfterAdd10 value = 
  6.     (mult5 << add10) value 

在 Elm 中 << 表示使用組合函數(shù),在上例中 value 傳給函數(shù) *** add10 *** 然后將其結(jié)果傳遞給 mult5。還可以這樣組合任意多個(gè)函數(shù):

  1. f x = 
  2.    (g << h << s << r << t) x 

這里 x 傳遞給函數(shù) t,函數(shù) t 的結(jié)果傳遞給 r,函數(shù) t 的結(jié)果傳遞給 s,以此類推。在Javascript中做類似的事情,它看起來(lái)會(huì)像 ***g(h(s(r(t(x)))))***,一個(gè)括號(hào)噩夢(mèng)。

常見(jiàn)的函數(shù)式函數(shù)(Functional Function)

函數(shù)式語(yǔ)言中3個(gè)常見(jiàn)的函數(shù):Map,Filter,Reduce。

如下JavaScript代碼:

  1. for (var i = 0; i < something.length; ++i) { 
  2.    // do stuff 

這段代碼存在一個(gè)很大的問(wèn)題,但不是bug。問(wèn)題在于它有很多重復(fù)代碼(boilerplate code)。如果你用命令式語(yǔ)言來(lái)編程,比如Java,C#,JavaScript,PHP,Python等等,你會(huì)發(fā)現(xiàn)這樣的代碼你寫(xiě)地最多。這就是問(wèn)題所在。

現(xiàn)在讓我們一步一步的解決問(wèn)題,最后封裝成一個(gè)看不見(jiàn) for 語(yǔ)法函數(shù):

先用名為 things 的數(shù)組來(lái)修改上述代碼:

  1. var things = [1, 2, 3, 4]; 
  2. for (var i = 0; i < things.length; ++i) { 
  3.     things[i] = things[i] * 10; // 警告:值被改變! 
  4. console.log(things); // [10, 20, 30, 40] 

這樣做法很不對(duì),數(shù)值被改變了!

在重新修改一次:

  1. var things = [1, 2, 3, 4]; 
  2. var newThings = []; 
  3. for (var i = 0; i < things.length; ++i) { 
  4.     newThings[i] = things[i] * 10; 
  5. console.log(newThings); // [10, 20, 30, 40] 

這里沒(méi)有修改***things***數(shù)值,但卻卻修改了***newThings***。暫時(shí)先不管這個(gè),畢竟我們現(xiàn)在用的是 JavaScript。一旦使用函數(shù)式語(yǔ)言,任何東西都是不可變的。

現(xiàn)在將代碼封裝成一個(gè)函數(shù),我們將其命名為 map,因?yàn)檫@個(gè)函數(shù)的功能就是將一個(gè)數(shù)組的每個(gè)值映射(map)到新數(shù)組的一個(gè)新值。

  1. var map = (f, array) => { 
  2.     var newArray = []; 
  3.     for (var i = 0; i < array.length; ++i) { 
  4.         newArray[i] = f(array[i]); 
  5.     } 
  6.     return newArray; 
  7. }; 

函數(shù) f 作為參數(shù)傳入,那么函數(shù) map 可以對(duì) array 數(shù)組的每項(xiàng)進(jìn)行任意的操作。

現(xiàn)在使用 map 重寫(xiě)之前的代碼:

  1. var things = [1, 2, 3, 4]; 
  2. var newThings = map(v => v * 10, things); 

這里沒(méi)有 for 循環(huán)!而且代碼更具可讀性,也更易分析。

現(xiàn)在讓我們寫(xiě)另一個(gè)常見(jiàn)的函數(shù)來(lái)過(guò)濾數(shù)組中的元素:

  1. var filter = (pred, array) => { 
  2.     var newArray = []; 
  3. for (var i = 0; i < array.length; ++i) { 
  4.         if (pred(array[i])) 
  5.             newArray[newArray.length] = array[i]; 
  6.     } 
  7.     return newArray; 
  8. }; 

當(dāng)某些項(xiàng)需要被保留的時(shí)候,斷言函數(shù) pred 返回TRUE,否則返回FALSE。

使用過(guò)濾器過(guò)濾奇數(shù):

  1. var isOdd = x => x % 2 !== 0; 
  2. var numbers = [1, 2, 3, 4, 5]; 
  3. var oddNumbers = filter(isOdd, numbers); 
  4. console.log(oddNumbers); // [1, 3, 5] 

比起用 for 循環(huán)的手動(dòng)編程,filter 函數(shù)簡(jiǎn)單多了。最后一個(gè)常見(jiàn)函數(shù)叫reduce。通常這個(gè)函數(shù)用來(lái)將一個(gè)數(shù)列歸約(reduce)成一個(gè)數(shù)值,但事實(shí)上它能做很多事情。

在函數(shù)式語(yǔ)言中,這個(gè)函數(shù)稱為 fold。

  1. var reduce = (f, start, array) => { 
  2.     var acc = start; 
  3.     for (var i = 0; i < array.length; ++i) 
  4.         acc = f(array[i], acc); // f() 有2個(gè)參數(shù) 
  5.     return acc; 
  6. }); 

reduce函數(shù)接受一個(gè)歸約函數(shù) f,一個(gè)初始值 start,以及一個(gè)數(shù)組 array。

這三個(gè)函數(shù),map,filter,reduce能讓我們繞過(guò)for循環(huán)這種重復(fù)的方式,對(duì)數(shù)組做一些常見(jiàn)的操作。但在函數(shù)式語(yǔ)言中只有遞歸沒(méi)有循環(huán),這三個(gè)函數(shù)就更有用了。附帶提一句,在函數(shù)式語(yǔ)言中,遞歸函數(shù)不僅非常有用,還必不可少。

作者:Fernando Doglio 譯者:前端小智 來(lái)源:medium

原文:https://blog.bitsrc.io/functional-programming-in-functions-composition-and-currying-3c765a50152e

本文轉(zhuǎn)載自微信公眾號(hào)「 大遷世界」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 大遷世界公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: 大遷世界
相關(guān)推薦

2011-10-19 15:47:13

2010-06-22 13:32:26

函數(shù)式編程JavaScript

2021-09-28 07:12:10

avaScriptCurrying柯里化

2012-03-21 09:30:11

ibmdw

2009-07-08 16:10:24

Scala簡(jiǎn)介面向?qū)ο?/a>函數(shù)式

2017-12-11 15:02:46

Javascript函數(shù)式編程currying

2020-12-03 08:23:23

函數(shù)柯里化代碼

2017-03-22 11:22:04

JavaScript函數(shù)式編程

2016-08-11 10:11:07

JavaScript函數(shù)編程

2016-08-11 10:34:37

Javascript函數(shù)編程

2017-10-26 08:53:38

前端JavaScript函數(shù)式編程

2023-10-07 00:01:02

Java函數(shù)

2023-08-02 08:01:14

柯里化反柯里化

2020-02-06 19:12:36

Java函數(shù)式編程編程語(yǔ)言

2023-05-06 07:27:47

2010-08-03 08:54:07

JDK 7Lambda表達(dá)式函數(shù)式編程

2023-11-21 07:17:36

Reac函數(shù)組件

2023-05-11 07:25:57

ReduxMiddleware函數(shù)

2013-09-09 09:41:34

2015-05-25 15:06:28

JavaScript函數(shù)式編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 日韩伦理一区二区 | 亚洲 欧美 另类 日韩 | www.国产精| 在线观看日韩精品视频 | 999久久久久久久 | 激情福利视频 | 精品欧美一区二区三区久久久小说 | 在线观看www | 欧美日韩在线免费观看 | 国产精品99久久久久久www | 成人在线中文 | 久久国产精品久久久久久 | 视频一二区 | 欧美8一10sex性hd | 日本精a在线观看 | 一级免费在线视频 | 日韩在线播放一区 | 密色视频 | 亚洲v日韩v综合v精品v | 中文av网站 | 国产人成在线观看 | 日韩精品1区2区3区 爱爱综合网 | 日韩电影免费在线观看中文字幕 | 国产一区久久久 | 二区视频 | 亚洲精品久久久蜜桃 | 午夜免费网站 | 欧美日韩在线看 | 国产精品欧美一区二区三区不卡 | 四虎影视1304t | 毛片网站在线观看视频 | 一区二区三区四区国产 | 久久中文字幕av | 国产精品美女久久久久久久网站 | 欧美成人免费在线视频 | 美女视频一区二区三区 | 九九色综合 | 国产精品久久久久9999鸭 | 日韩视频一区在线观看 | 欧美综合视频 | 日韩欧美国产一区二区三区 |