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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java函数式编程_说说函数式编程的那些事

發(fā)布時間:2025/3/19 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java函数式编程_说说函数式编程的那些事 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

今天這篇文章我們主要來聊聊函數(shù)式編程的思想。

  • 函數(shù)式編程有用嗎?
  • 什么是函數(shù)式編程?
  • 函數(shù)式編程的優(yōu)點。

總所周知 JavaScript 是一種擁有很多共享狀態(tài)的動態(tài)語言,慢慢的,代碼就會積累足夠的復(fù)雜性,變得笨拙難以維護(hù)。面向?qū)ο笤O(shè)計能幫我們在一定程度上解決這個問題,但是還不夠。

由于有很多的狀態(tài),所以處理數(shù)據(jù)流和變化的傳遞顯得尤為重要,不知道你們知道響應(yīng)式編程與否,這種編程范式有助于處理 JavaScript 的異步或者事件響應(yīng)。總之,當(dāng)我們在設(shè)計應(yīng)用程序的時候,我們應(yīng)該考慮是否遵守了以下的設(shè)計原則。

  • 可擴(kuò)展性--我是否需要不斷地重構(gòu)代碼來支持額外的功能?
  • 易模塊化--如果我更改了一個文件,另一個文件是否會受到影響?
  • 可重用性--是否有很多重復(fù)的代碼?
  • 可測性--給這些函數(shù)添加單元測試是否讓我糾結(jié)?
  • 易推理性--我寫的代碼是否非結(jié)構(gòu)化嚴(yán)重并難以推理?

我這能這么跟你說,一旦你學(xué)會了函數(shù)式編程,這些問題迎刃而解,本來函數(shù)式編程就是這個思想,一旦你掌握了函數(shù)式,然后你再學(xué)習(xí)響應(yīng)式編程那就比較容易懂了,這是我親身體會的。那下面我們就開始函數(shù)式編程之旅。

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

簡單來說,函數(shù)式編程是一種強(qiáng)調(diào)以函數(shù)使用為主的軟件開發(fā)風(fēng)格。看到這句我想你還是一臉懵逼,不知道函數(shù)式編程是啥,不要著急,看到最后我相信你會明白的。

還有一點你要記住,函數(shù)式編程的目的是使用函數(shù)來抽象作用在數(shù)據(jù)之上的控制流和操作,從而在系統(tǒng)中消除副作用并減少對狀態(tài)的改變。

下面我們通過例子來簡單的演示一下函數(shù)式編程的魅力。

現(xiàn)在的需求就是輸出在網(wǎng)頁上輸出 “Hello World”。

可能初學(xué)者會這么寫。

document.querySelector('#msg').innerHTML = '

Hello World

'

這句代碼沒錯,也很簡單,但是所有代碼都是死的,不能重用,如果想改變消息的格式、內(nèi)容等就需要重寫整個表達(dá)式,所以可能有經(jīng)驗的前端開發(fā)者會這么寫。

function printMessage(elementId, format, message) { document.querySelector(elementId).innerHTML = `${message}${format}>`}printMessage('msg', 'h1', 'Hello World')

這樣確實有所改進(jìn),但是任然不是一段可重用的代碼,如果是要將文本寫入文件,不是非 HTML,或者我想重復(fù)的顯示 Hello World。

那么作為一個函數(shù)式開發(fā)者會怎么寫這段代碼呢?

//實現(xiàn)一個compose函數(shù)function compose() { var fns = [].slice.call(arguments) return function (initialArg) { var res = initialArg for (var i = fns.length - 1; i > -1; i--) { res = fns[i](res) } return res }}const printMessage = compose(addToDom('msg'), h1, echo)printMessage('Hello World')

解釋一下這段代碼,其中的 h1 和 echo 都是函數(shù),addToDom 很明顯也能看出它是函數(shù),表示把文本插入DOM中,代碼我就省了。那么我們?yōu)槭裁匆獙懗蛇@樣呢?看起來多了很多函數(shù)一樣。

其實我們是講程序分解為一些更可重用、更可靠且更易于理解的部分,然后再將他們組合起來,形成一個更易推理的程序整體,這是我們前面談到的基本原則。

compose 簡單解釋一下,他會讓函數(shù)從最后一個參數(shù)順序執(zhí)行到第一個參數(shù),compose 的每個參數(shù)都是函數(shù),初始函數(shù)一定放到參數(shù)的最右面。

可以看到我們是將一個任務(wù)拆分成多個最小顆粒的函數(shù),然后通過組合的方式來完成我們的任務(wù),這跟我們組件化的思想很類似,將整個頁面拆分成若干個組件,然后拼裝起來完成我們的整個頁面。在函數(shù)式編程里面,組合是一個非常非常非常重要的思想

好,我們現(xiàn)在再改變一下需求,現(xiàn)在我們需要將文本重復(fù)三遍,打印到控制臺。

var printMessaage = compose(console.log, repeat(3), echo)printMessage(‘Hello World’)

可以看到我們更改了需求并沒有去修改內(nèi)部邏輯,只是重組了一下函數(shù)而已。

可以看到函數(shù)式編程在開發(fā)中具有聲明模式。為了充分理解函數(shù)式編程,我們先來看下幾個基本概念。

  • 聲明式編程
  • 純函數(shù)
  • 引用透明
  • 不可變性

聲明式編程

函數(shù)式編程屬于聲明是編程范式:這種范式會描述一系列的操作,但并不會暴露它們是如何實現(xiàn)的或是數(shù)據(jù)流如何傳過它們。

我們所熟知的 SQL 語句就是一種很典型的聲明式編程,它由一個個描述查詢結(jié)果應(yīng)該是什么樣的斷言組成,對數(shù)據(jù)檢索的內(nèi)部機(jī)制進(jìn)行了抽象。

我們再來看一組代碼再來對比一下命令式編程和聲明式編程。

// 命令式方式var array = [0, 1, 2, 3]for(let i = 0; i < array.length; i++) { array[i] = Math.pow(array[i], 2)}array; // [0, 1, 4, 9]// 聲明式方式[0, 1, 2, 3].map(num => Math.pow(num, 2))

可以看到命令式很具體的告訴計算機(jī)如何執(zhí)行某個任務(wù)。

而聲明式是將程序的描述與求值分離開來。它關(guān)注如何用各種表達(dá)式來描述程序邏輯,而不一定要指明其控制流或狀態(tài)關(guān)系的變化。

為什么我們要去掉代碼循環(huán)呢?循環(huán)是一種重要的命令控制結(jié)構(gòu),但很難重用,并且很難插入其他操作中。而函數(shù)式編程旨在盡可能的提高代碼的無狀態(tài)性和不變性。要做到這一點,就要學(xué)會使用無副作用的函數(shù)--也稱純函數(shù)

純函數(shù)

純函數(shù)指沒有副作用的函數(shù)。相同的輸入有相同的輸出,就跟我們上學(xué)的函數(shù)一樣。

常常這些情況會產(chǎn)生副作用。

  • 改變一個全局的變量、屬性或數(shù)據(jù)結(jié)構(gòu)
  • 改變一個函數(shù)參數(shù)的原始值
  • 處理用戶輸入
  • 拋出一個異常
  • 屏幕打印或記錄日志
  • 查詢 HTML 文檔,瀏覽器的 Cookie 或訪問數(shù)據(jù)庫

舉一個簡單的例子

var counter = 0function increment() { return ++counter;}

這個函數(shù)就是不純的,它讀取了外部的變量,可能會覺得這段代碼沒有什么問題,但是我們要知道這種依賴外部變量來進(jìn)行的計算,計算結(jié)果很難預(yù)測,你也有可能在其他地方修改了 counter 的值,導(dǎo)致你 increment 出來的值不是你預(yù)期的。

對于純函數(shù)有以下性質(zhì):

  • 僅取決于提供的輸入,而不依賴于任何在函數(shù)求值或調(diào)用間隔時可能變化的隱藏狀態(tài)和外部狀態(tài)。
  • 不會造成超出作用域的變化,例如修改全局變量或引用傳遞的參數(shù)。

但是在我們平時的開發(fā)中,有一些副作用是難以避免的,與外部的存儲系統(tǒng)或 DOM 交互等,但是我們可以通過將其從主邏輯中分離出來,使他們易于管理。

現(xiàn)在我們有一個小需求:通過 id 找到學(xué)生的記錄并渲染在瀏覽器(在寫程序的時候要想到可能也會寫到控制臺,數(shù)據(jù)庫或者文件,所以要想如何讓自己的代碼能重用)中。

如果看不懂 curry (柯里化)的先不著急,這是一個對于新手來說比較難理解的一個概念,在函數(shù)式編程里面起著至關(guān)重要的作用。

可以看到函數(shù)式代碼通過較少這些函數(shù)的長度,將 showStudent 編寫為小函數(shù)的組合。這個程序還不夠完美,但是已經(jīng)可以展現(xiàn)出相比于命令式的很多優(yōu)勢了。

  • 靈活。有三個可重用的組件
  • 聲明式的風(fēng)格,給高階步驟提供了一個清晰視圖,增強(qiáng)了代碼的可讀性
  • 另外是將純函數(shù)與不純的行為分離出來。

我們看到純函數(shù)的輸出結(jié)果是一致的,可預(yù)測的,相同的輸入會有相同的返回值,這個其實也被稱為引用透明。

引用透明

引用透明是定義一個純函數(shù)較為正確的方法。純度在這個意義上表面一個函數(shù)的參數(shù)和返回值之間映射的純的關(guān)系。如果一個函數(shù)對于相同的輸入始終產(chǎn)生相同的結(jié)果,那么我們就說它是引用透明。

這個概念很容易理解,簡單的舉兩個例子就行了。

// 非引用透明var counter = 0function increment() { return ++counter}// 引用透明var increment = (counter) => counter + 1

其實對于箭頭函數(shù)在函數(shù)式編程里面有一個高大上的名字,叫 lambda 表達(dá)式,對于這種匿名函數(shù)在學(xué)術(shù)上就是叫 lambda 表達(dá)式,現(xiàn)在在 Java 里面也是支持的。

不可變數(shù)據(jù)

不可變數(shù)據(jù)是指那些創(chuàng)建后不能更改的數(shù)據(jù)。與許多其他語言一樣,JavaScript 里有一些基本類型(String,Number 等)從本質(zhì)上是不可變的,但是對象就是在任意的地方可變。

考慮一個簡單的數(shù)組排序代碼:

var sortDesc = function(arr) { return arr.sort(function(a, b) { return a - b })}var arr = [1, 3, 2]sortDesc(arr) // [1, 2, 3]arr // [1, 2, 3]

這段代碼看似沒什么問題,但是會導(dǎo)致在排序的過程中會產(chǎn)生副作用,修改了原始引用,可以看到原始的 arr 變成了 [1, 2, 3]。這是一個語言缺陷,后面會介紹如何克服。

小結(jié)

下面我們來總結(jié)一下這篇文章的主要知識點:

  • 使用純函數(shù)的代碼絕不會更改或破壞全局狀態(tài),有助于提高代碼的可測試性和可維護(hù)性
  • 函數(shù)式編程采用聲明式的風(fēng)格,易于推理,提高代碼的可讀性。
  • 函數(shù)式編程將函數(shù)視為積木,通過一等高階函數(shù)來提高代碼的模塊化和可重用性。
  • 可以利用響應(yīng)式編程組合各個函數(shù)來降低事件驅(qū)動程序的復(fù)雜性(這點后面可能會單獨拿一篇來進(jìn)行講解)。

本篇內(nèi)容可能不太容易理解,需要大伙多看幾遍,并且要多寫多理解,這樣可以更好的實現(xiàn)思想轉(zhuǎn)變。

總結(jié)

以上是生活随笔為你收集整理的java函数式编程_说说函数式编程的那些事的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。