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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[译]Reduce(软件编写)(第五部分)

發布時間:2025/4/5 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [译]Reduce(软件编写)(第五部分) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
  • 原文地址:Reduce (Composing Software)(part 5)
  • 原文作者:Eric Elliott
  • 譯文出自:掘金翻譯計劃
  • 譯者:yoyoyohamapi
  • 校對者:avocadowang Aladdin-ADD

Smoke Art Cubes to Smoke?—?MattysFlicks?—?(CC BY 2.0) (譯注:該圖是用 PS 將煙霧處理成方塊狀后得到的效果,參見 flickr。))

注意:這是 “軟件編寫” 系列文章的第五部分,該系列主要闡述如何在 JavaScript ES6+ 中從零開始學習函數式編程和組合化軟件(compositional software)技術(譯注:關于軟件可組合性的概念,參見維基百科 Composability)。后續還有更多精彩內容,敬請期待!

<上一篇 | << 返回第一篇

在函數式編程中,reduce(也稱為:fold,accumulate)允許你在一個序列上迭代,并應用一個函數來處理預先聲明的累積值和當前迭代到的元素。當迭代完成時,將返回這個累積值。許多其他有用的功能都可以通過 reduce 實現。多數時候,reduce 可以說是處理集合(collection)最優雅的方式。

reduce 接受一個 reducer 函數以及一個初始值,最終返回一個累積值。對于 Array.prototype.reduce() 來說, 初始列表將由 this 指明, 所以列表本身不會作為該函數的參數:

array.reduce(reducer: (accumulator: Any, current: Any) => Any,initialValue: Any ) => accumulator: Any復制代碼

我們利用如下方式對一個數組進行求和:

[2, 4, 6].reduce((acc, n) => acc + n, 0); // 12復制代碼

對于數組的每步迭代,reducer 函數都會被調用,并且向其傳入了累積值和當前迭代到的數組元素。reducer 的職責在于以某種方式將當前迭代的元素 “合攏(fold)” 到累加值中。reducer 規定了 “合攏” 的手段和方式,完成了對當前元素的 “合攏” 后,reducer 將返回新的累加值,然后, .reduce() 將開始處理數組中的下一個元素。reducer 需要一個初始值才能開始工作,所以絕大多數的 .reduce() 實現都需要接收一個初始值作為參數。

在數組元素求和一例中,reducer 函數第一次調用時,acc 將會以 0 值(該值是傳入 .reduce() 方法的第二個參數)開始。然后,reducer 返回了 0 + 2(2 是數組的第一個元素), 也就是返回了 2 作為新的累積值。下一步,acc = 2, n = 4 傳入了 reducer,reducer返回了 2 + 4(6)。在最后一步迭代中,acc = 6, n = 6, reducer 返回了 12。迭代完成,.reduce() 返回了最終的累積值 12。

在這一例子中,我們傳入了一個匿名函數作為 reducer,但是我們也可以抽象出每次求和的過程為一個具名函數,這使得我們代碼的復用程度更高:

const summingReducer = (acc, n) => acc + n; [2, 4, 6].reduce(summingReducer, 0); // 12復制代碼

通常,reduce 的工作過程為由左向右。在 JavaScript 中,我們也有一個 [].reduceRight() (譯注:MDN -- Array.prototype.reduceRight())方法來讓 reduce 由右向左地工作。 具體說來,如果你對數組 [2, 4, 6] 應用 .reduceRight() ,第一個被迭代到的元素就將是 6,最后一個迭代到的元素就是 2。

無所不能的 reduce

別吃驚,reduce 確實無所不能,你所熟悉的 map(),filter(),forEach() 以及其他函數都可借助于 reduce 來創建。

Map:

const map = (fn, arr) => arr.reduce((acc, item, index, arr) => {return acc.concat(fn(item, index, arr)); }, []);復制代碼

對于 map 來說,我們的累積值就是一個新的數組對象,該數組對象中的每個元素都由原數組對應元素映射得到。累積數組中新的元素由傳入 map 的映射函數(fn)所確定:對于當前迭代到的元素 item,我們通過 fn 計算出新的元素,并將其拼接入累加數組 acc 中。

Filter:

const filter = (fn, arr) => arr.reduce((newArr, item) => {return fn(item) ? newArr.concat([item]) : newArr; }, []);復制代碼

filter 的工作方式與 map 類似,只不過原數組的元素只有通過一個真值檢測函數(predicate function)才能被送入新的累積數組中。亦即,相較于 map,filter 是有條件地選擇元素到累積數組中,并且不會改變元素的值。

上面幾個例子,你處理的數據都是一些數值序列,你在數值序列上應用指定的函數迭代數據,并將結果合攏到累積值中。大多數應用都因此開始雛形初備,但是你想過這個問題:假如你的序列是函數序列呢?

Compose:

reduce 也是實現函數組合的便捷渠道。假如你想用將函數 g 的輸出作為函數 f 的輸入,即組合這兩個函數: f . g,那么你可以使用下面的 JavaScript 代碼片,它沒有任何的抽象:

f(g(x))復制代碼

reduce 讓我們能抽象出函數組合過程,從而讓你也能輕易地實現更多層次的函數組合:

f(g(h(x)))復制代碼

為了使函數組合是由右向左的,我們就要使用上面提到的 .reduceRight() 方法來抽象函數組合過程:

const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);復制代碼

注意:如果 JavaScript 的版本沒有提供 [].reduceRight(),你可以借助于 reduce 實現該方法。該實現留給讀者自己思考。

Pipe:

compose() 很好地描述了由內至外的組合過程,某種程度上,這是數學上的關于輸入輸出的組合。如果你想從事件發生順序上來思考函數組合呢?

假設我們想要對一個數值加 1,然后對新得到的數值進行翻倍。如果是利用 compose(),就需要這么做:

const add1 = n => n + 1; const double = n => n * 2;const add1ThenDouble = compose(double,add1 );add1ThenDouble(2); // 6 // ((2 + 1 = 3) * 2 = 6)復制代碼

發現問題沒有?第一步(加1操作)是 compose 序列上的最后一個元素,所以,compose 需要你自底向上地分析流程的執行。

我們使用 reduce 由左向右的常用特性取代由右向左的組合方式,以示區別,我們用 pipe 來描述新的組合方式:

const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);復制代碼

現在,新的流程就可以這么撰寫:

const add1ThenDouble = pipe(add1,double );add1ThenDouble(2); // 6 // ((2 + 1 = 3) * 2 = 6)復制代碼

如你所見,在組合中,順序是非常重要的,如果你調換了 double 和 add1 的順序,你將得到截然不同的結果:

const doubleThenAdd1 = pipe(double,add1 );doubleThenAdd1(2); // 5復制代碼

之后,我們還會討論跟多的關于 compose() 和 pipe() 的細節。現在,你所要知道的只是,reduce() 是一個極為強大的工具,因此一定要掌握它。 如果在學習過程中遇到了挫折,也大可不必灰心,很多開發者都花了大量時間才能掌握 reduce。

Redux 中的 reduce

你可能聽說過 “reducer” 這個術語被用于描述 Redux 的狀態更新。這篇文章撰寫之時,對于使用了 React 或者 Angular 進行構建的 web 應用來說,Redux 是最流行的狀態管理庫/架構(Angualar 中的類 Redux 管理是 ngrx/store )。

Redux 使用了 reducer 函數來管理應用狀態。一個 Redux 風格的 reducer 接收一個當前應用狀態 state 和 和交互對象 action 作為參數(譯注:當前狀態就相當于累積值,而 action 就相當于目前處理的元素),處理完成后,返回一個新的應用狀態:

reducer(state: Any, action: { type: String, payload: Any}) => newState: Any復制代碼

Redux 的一些 reducer 規則需要你牢記在心:

  • 一個 reducer 如果進行了無參調用,它要返回它的初始狀態。
  • 如果 reducer 操縱的 action 沒有聲明類型,他要返回當前狀態。
  • 最最重要的是,Redux reducer 必須是純函數。
  • 現在,我們以 Redux 風格重寫上面的求和 reducer,該 reducer 的行為將由 action 類型決定:

    const ADD_VALUE = 'ADD_VALUE';const summingReducer = (state = 0, action = {}) => {const { type, payload } = action;switch (type) {case ADD_VALUE:return state + payload.value;default: return state;} };復制代碼

    關于 Redux 的一個非常美妙的事兒就是,其 reducer 都是標準的 reducer (譯注:即接收 accumulator 和 current 兩個參數的 reducer ),這意味著你將 Redux 中的 reducer 插入到任何現有的 reduce() 實現中去,比如最常用的 [].reduce()。以此為例,我們可以創建一個 action 對象的數組,并對其進行 reduce 操作,傳入 reduce() 的將是我們定義好的 summingReducer,據此,我們獲得一個狀態快照。之后,一旦對 Redux 中的狀態樹(store)分派了同樣的 action 序列,那么一定能俘獲到相同的狀態快照:

    const actions = [{ type: 'ADD_VALUE', payload: { value: 1 } },{ type: 'ADD_VALUE', payload: { value: 1 } },{ type: 'ADD_VALUE', payload: { value: 1 } }, ];actions.reduce(summingReducer, 0); // 3復制代碼

    這使得對 Redux 風格的 reducer 的單元測試變得極為容易。

    總結

    現在,你應該可以瞥見 reduce 的強大甚至是無所不能了。雖然,理解 reduce 要比理解 map 或者 filter 難一些,還是函數式編程中重要的工具,這個工具強大在它是一個基礎工具,能夠通過它構建出更多更強大的工具。

    下一篇: Functors 與 Categories >

    接下來

    想學習更多 JavaScript 函數式編程嗎?

    跟著 Eric Elliott 學 Javacript,機不可失時不再來!

    Eric Elliott“編寫 JavaScript 應用” (O’Reilly) 以及 “跟著 Eric Elliott 學 Javascript” 兩書的作者。他為許多公司和組織作過貢獻,例如 Adobe SystemsZumba FitnessThe Wall Street JournalESPNBBC 等 , 也是很多機構的頂級藝術家,包括但不限于 UsherFrank Ocean 以及 Metallica

    大多數時間,他都在 San Francisco Bay Area,同這世上最美麗的女子在一起。


    掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、React、前端、后端、產品、設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃。

    總結

    以上是生活随笔為你收集整理的[译]Reduce(软件编写)(第五部分)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。