高級函數(shù)式編程:使用流管道優(yōu)化數(shù)據(jù)處理性能

言鼎科技 2023-05-05 423
高級函數(shù)式編程:使用流管道優(yōu)化數(shù)據(jù)處理性能

先決條件

  • 牢牢掌握 ES6,尤其是箭頭函數(shù)、生成器、Array.map、Array.reduce ……

  • 對柯里化、純函數(shù)、高階函數(shù)等函數(shù)式編程有深刻的理解, 

  • RxJS 的基礎(chǔ)知識。

注意:本文著重于高級函數(shù)式編程,因此請確保您在深入了解主要內(nèi)容之前滿足要求。

目錄

一、 功能構(gòu)成

2. 換能器

3.  RxJS 用例

功能組合

什么是函數(shù)組合,我們?yōu)槭裁葱枰?/span>

在計算機科學(xué)中,函數(shù)組合是一種組合簡單功能以構(gòu)建更復(fù)雜功能的行為或機制

組合函數(shù)可幫助開發(fā)人員將復(fù)雜的邏輯分解為一組較小的問題,從而顯著提高可維護(hù)性和代碼重用性。

一個真實世界的例子來進(jìn)一步理解:

想象一下,您是一名調(diào)酒師,其工作是以有序的方式混合烈酒、飲料和許多其他類型的配料來提供雞尾酒。

你有一個空杯子 讓 glass = "" 作為初始值和多個準(zhǔn)備步驟:

請記住,成分和您采取的步驟很重要,因為它會產(chǎn)生不同的飲料:

帕洛瑪雞尾酒配方

對于每份雞尾酒配方,我們都需要編寫一個函數(shù)來準(zhǔn)備它,其中包含需要時間執(zhí)行的各種準(zhǔn)備步驟。所以你開始有了一個想法:實現(xiàn)一個樸素的let 循環(huán),以有序的方式執(zhí)行準(zhǔn)備步驟。

更短,更容易閱讀!

如同數(shù)學(xué)中的函數(shù)組合,每個函數(shù)的結(jié)果作為下一個函數(shù)的參數(shù)傳遞,最后一個函數(shù)的結(jié)果是整體的結(jié)果。

這種命令式實現(xiàn)在函數(shù)組合方面已經(jīng)足夠好了,但因為我們正在學(xué)習(xí)函數(shù)式編程,所以讓我們通過使用Arrray.reduce  來簡化serveCocktail 函數(shù),使其成為聲明式的:

onst serveCocktail = (...方法) => {
  // 初始值被硬編碼為空字符串
  return methods.reduce((glass, method) => 方法(glass), "")}

更好的是,我們可以使用柯里化來提升初始值作為參數(shù)。對于通用用途,我們定義了一個實用函數(shù),使我們可以輕松地組合函數(shù)。

  • pipe : 從左到右組合函數(shù)。

  • compose : 從右到左組合函數(shù)。

在pipe和 compose的幫助下,組合函數(shù)要容易得多 。

當(dāng)我們將一個復(fù)雜的過程分解成更小的函數(shù)時,每個函數(shù)只做一項工作,開發(fā)人員可以編寫聲明性代碼來增強可讀性、可重用性和關(guān)注點分離。

 

傳感器

transducer 是一個可組合的高階 reducer。它以一個 reducer 作為輸入,并返回另一個 reducer(埃里克·埃利奧特)

reducer是一個純函數(shù),它接受 2 個參數(shù)并返回一個新值。

在循環(huán)上下文中應(yīng)用reducer時:

  • 名為accumulator的第一個參數(shù)是上一步的結(jié)果,如果它是循環(huán)中的第一步,則為初始值。

  • 第二個參數(shù)是循環(huán)中的當(dāng)前值。

所以reducer的簡單語法如下:

reducer = (accumulator, curVal) => newVal

然后 transducer 的語法可以表示為:

換能器 = 減速器 => 減速器

回答“為什么我們需要換能器?”這個問題 ,跟我舉個例子:

想象一下,作為 NASA 的一名科學(xué)家,你的職責(zé)是通過接收信號與我們星球外的外星飛船進(jìn)行通信,并將其轉(zhuǎn)換為人類可理解的數(shù)據(jù),然后顯示最終信息。

高級函數(shù)式編程:使用流管道優(yōu)化數(shù)據(jù)處理性能

  1. 轉(zhuǎn)換信號:過濾虛假信號(噪聲),然后將信號轉(zhuǎn)換為字符串。

  2. 顯示消息:連接字符串。

只有當(dāng)你的數(shù)組很小時,這個簡單但天真的實現(xiàn)才合適。

考慮處理大量的萬億信號:

  • filter 每次使用and遍歷數(shù)組時map ,JavaScript 都會啟動一個全新的中間數(shù)組,它會占用大量內(nèi)存。

  • 在執(zhí)行下一個之前,您需要等待每個處理步驟完成。在上面的代碼中,如果我們要檢索最終的消息,我們必須等待reduce 它完成。在此之前,reduce 必須等待map 完成。在此之前,map 必須等待filter 完成。這是一個致命的問題,因為消息中的信息非常關(guān)鍵,我們必須在非常嚴(yán)格的期限內(nèi)閱讀它。

解決這個問題的一種方法是構(gòu)建一個處理管道,將信號作為數(shù)據(jù)流,并以正確的順序?qū)γ總€信號執(zhí)行。

稍微重寫一下,以便您可以識別流程xform 與函數(shù)組合完全相同。的結(jié)果decodeSignal 取決于isTruthful,而 的結(jié)果concatString 取決于decodeSignal。

(味精,信號)=> {
 讓 val = isTruthful(sig) ? 簽名:空
 如果(值){
   val = decodeSignal(val)
   val = concatString(msg, val)
   返回值
 }
 返回消息}

在這個例子中,我們只需要處理幾個處理步驟。但請記住,在現(xiàn)實世界中,信號轉(zhuǎn)換過程必須以復(fù)雜的順序經(jīng)歷多個步驟。為了構(gòu)建靈活的管道,我們開始想到函數(shù)組合:

撰寫(isTruthful,decodeSignal,concatString)//不工作

但它不會起作用,僅僅是因為這些函數(shù)不可組合。換句話說,前一個函數(shù)的結(jié)果與下一個函數(shù)的參數(shù)不兼容。

為了使鏈接成為可能,我們必須重寫 filter and map 函數(shù):

const filter = next => {
 返回 (msg, sig) => isTruthful(sig) ?下一個(味精,信號):味精} const map = next => {
 返回(味精,信號)=>下一個(味精,解碼信號(信號))}

假設(shè)那next 是一個reducer,那么filter map transducer,它們將一個reducer作為輸入并返回另一個reducer。

為了增強可重用性,我們使用柯里化來提升isTruthful 和decodeSignal 作為參數(shù):

const filter = predicate => next =>
        (acc, cur) =>謂詞(cur) ? 下一個(acc,cur):acc const map = transform => next =>
     (acc, cur) => next(acc, transform (cur)) const transformSignals = compose (    filter ( isTruthful ),    map ( decodeSignal )}

我們可以使用compose 組合 傳感器 ,因為它們是可組合的,結(jié)果 transformSignals 是一個新的 傳感器,所以我們必須提供一個縮減器作為最后一步,告訴 傳感器 如何累積結(jié)果。

const xform = transformSignals( concatString ) const message = signals.reduce( xform , "")

總結(jié)我們剛剛所做的一切,這是您可以運行的完整代碼:

 

RxJS 用例

恭喜?。?!如果您正在閱讀本節(jié),那么您已經(jīng)完成了所有最難的部分。RxJS 是一個支持流數(shù)據(jù)的庫,它有許多類似的轉(zhuǎn)換器filter ,map 因此我們不必從頭開始實現(xiàn)任何東西。

嘗試運行并觀察輸出,可以清楚地看到每個信號都是獨立處理的,并立即打印出結(jié)果。

言鼎科技

The End