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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

翻译连载 | 附录 A:Transducing(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇...

發(fā)布時間:2024/7/19 javascript 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 翻译连载 | 附录 A:Transducing(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
  • 原文地址:Functional-Light-JS
  • 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者

關(guān)于譯者:這是一個流淌著滬江血液的純粹工程:認(rèn)真,是 HTML 最堅實的梁柱;分享,是 CSS 里最閃耀的一瞥;總結(jié),是 JavaScript 中最嚴(yán)謹(jǐn)?shù)倪壿嫛=?jīng)過捶打磨練,成就了本書的中文版。本書包含了函數(shù)式編程之精髓,希望可以幫助大家在學(xué)習(xí)函數(shù)式編程的道路上走的更順暢。比心。

譯者團隊(排名不分先后):阿希、blueken、brucecham、cfanlife、dail、kyoko-df、l3ve、lilins、LittlePineapple、MatildaJin、冬青、pobusama、Cherry、蘿卜、vavd317、vivaxy、萌萌、zhouyao

JavaScript 輕量級函數(shù)式編程

附錄 A:Transducing(下)

組合柯里化

這一步是最棘手的。所以請慢慢的用心的閱讀。

讓我們看看沒有將 listCombination(..) 傳遞給柯里化函數(shù)的樣子:

var x = curriedMapReducer( strUppercase ); var y = curriedFilterReducer( isLongEnough ); var z = curriedFilterReducer( isShortEnough );

看看這三個中間函數(shù) x(..), y(..) 和 z(..)。每個函數(shù)都期望得到一個單一的組合函數(shù)并產(chǎn)生一個 reducer 函數(shù)。

記住,如果我們想要所有這些的獨立的 reducer,我們可以這樣做:

var upperReducer = x( listCombination ); var longEnoughReducer = y( listCombination ); var shortEnoughReducer = z( listCombination );

但是,如果你調(diào)用 y(z),會得到什么呢?當(dāng)把 z 傳遞給 y(..) 調(diào)用,而不是 combinationFn(..) 時會發(fā)生什么呢?這個返回的 reducer 函數(shù)內(nèi)部看起來像這樣:

function reducer(list,val) {if (isLongEnough( val )) return z( list, val );return list; }

看到 z(..) 里面的調(diào)用了嗎? 這看起來應(yīng)該是錯誤的,因為 z(..) 函數(shù)應(yīng)該只接收一個參數(shù)(combinationFn(..)),而不是兩個參數(shù)(list 和 val)。這和要求不匹配。不行。

我們來看看組合 y(z(listCombination))。我們將把它分成兩個不同的步驟:

var shortEnoughReducer = z( listCombination ); var longAndShortEnoughReducer = y( shortEnoughReducer );

我們創(chuàng)建 shortEnoughReducer(..),然后將它作為 combinationFn(..) 傳遞給 y(..),生成 longAndShortEnoughReducer(..)。多讀幾遍,直到理解。

現(xiàn)在想想: shortEnoughReducer(..) 和 longAndShortEnoughReducer(..) 的內(nèi)部構(gòu)造是什么樣的呢?你能想得到嗎?

// shortEnoughReducer, from z(..): function reducer(list,val) {if (isShortEnough( val )) return listCombination( list, val );return list; }// longAndShortEnoughReducer, from y(..): function reducer(list,val) {if (isLongEnough( val )) return shortEnoughReducer( list, val );return list; }

你看到 shortEnoughReducer(..) 替代了 longAndShortEnoughReducer(..) 里面 listCombination(..) 的位置了嗎? 為什么這樣也能運行?

因為 reducer(..) 的“形狀”和 listCombination(..) 的形狀是一樣的。 換句話說,reducer 可以用作另一個 reducer 的組合函數(shù); 它們就是這樣組合起來的! listCombination(..) 函數(shù)作為第一個 reducer 的組合函數(shù),這個 reducer 又可以作為組合函數(shù)給下一個 reducer,以此類推。

我們用幾個不同的值來測試我們的 longAndShortEnoughReducer(..) :

longAndShortEnoughReducer( [], "nope" ); // []longAndShortEnoughReducer( [], "hello" ); // ["hello"]longAndShortEnoughReducer( [], "hello world" ); // []

longAndShortEnoughReducer(..) 會過濾出不夠長且不夠短的值,它在同一步驟中執(zhí)行這兩個過濾。這是一個組合 reducer!

再花點時間消化下。

現(xiàn)在,把 x(..) (生成大寫 reducer 的產(chǎn)生器)加入組合:

var longAndShortEnoughReducer = y( z( listCombination) ); var upperLongAndShortEnoughReducer = x( longAndShortEnoughReducer );

正如 upperLongAndShortEnoughReducer(..) 名字所示,它同時執(zhí)行所有三個步驟 - 一個映射和兩個過濾器!它內(nèi)部看起來是這樣的:

// upperLongAndShortEnoughReducer: function reducer(list,val) {return longAndShortEnoughReducer( list, strUppercase( val ) ); }

一個字符串類型的 val 被傳入,由 strUppercase(..) 轉(zhuǎn)換成大寫,然后傳遞給 longAndShortEnoughReducer(..)。該函數(shù)只有在 val 滿足足夠長且足夠短的條件時才將它添加到數(shù)組中。否則數(shù)組保持不變。

我花了幾個星期來思考分析這種雜耍似的操作。所以別著急,如果你需要在這好好研究下,重新閱讀個幾(十幾個)次。慢慢來。

現(xiàn)在來驗證一下:

upperLongAndShortEnoughReducer( [], "nope" ); // []upperLongAndShortEnoughReducer( [], "hello" ); // ["HELLO"]upperLongAndShortEnoughReducer( [], "hello world" ); // []

這個 reducer 成功的組合了和 map 和兩個 filter,太棒了!

讓我們回顧一下我們到目前為止所做的事情:

var x = curriedMapReducer( strUppercase ); var y = curriedFilterReducer( isLongEnough ); var z = curriedFilterReducer( isShortEnough );var upperLongAndShortEnoughReducer = x( y( z( listCombination ) ) );words.reduce( upperLongAndShortEnoughReducer, [] ); // ["WRITTEN","SOMETHING"]

這已經(jīng)很酷了,但是我們可以讓它更好。

x(y(z( .. ))) 是一個組合。我們可以直接跳過中間的 x / y / z 變量名,直接這么表示該組合:

var composition = compose(curriedMapReducer( strUppercase ),curriedFilterReducer( isLongEnough ),curriedFilterReducer( isShortEnough ) );var upperLongAndShortEnoughReducer = composition( listCombination );words.reduce( upperLongAndShortEnoughReducer, [] ); // ["WRITTEN","SOMETHING"]

我們來考慮下該組合函數(shù)中“數(shù)據(jù)”的流動:

  • listCombination(..) 作為組合函數(shù)傳入,構(gòu)造 isShortEnough(..) 過濾器的 reducer。

  • 然后,所得到的 reducer 函數(shù)作為組合函數(shù)傳入,繼續(xù)構(gòu)造 isShortEnough(..) 過濾器的 reducer。

  • 最后,所得到的 reducer 函數(shù)作為組合函數(shù)傳入,構(gòu)造 strUppercase(..) 映射的 reducer。

  • 在前面的片段中,composition(..) 是一個組合函數(shù),期望組合函數(shù)來形成一個 reducer;而這個 composition(..) 有一個特殊的標(biāo)簽:transducer。給 transducer 提供組合函數(shù)產(chǎn)生組合的 reducer:

    // TODO:檢查 transducer 是產(chǎn)生 reducer 還是它本身就是 reducer

    var transducer = compose(curriedMapReducer( strUppercase ),curriedFilterReducer( isLongEnough ),curriedFilterReducer( isShortEnough ) );words .reduce( transducer( listCombination ), [] ); // ["WRITTEN","SOMETHING"]

    注意:我們應(yīng)該好好觀察下前面兩個片段中的 compose(..) 順序,這地方有點難理解。回想一下,在我們的原始示例中,我們先 map(strUppercase) 然后 filter(isLongEnough) ,最后 filter(isShortEnough);這些操作實際上也確實按照這個順序執(zhí)行的。但在第 4 章中,我們了解到,compose(..) 通常是以相反的順序運行。那么為什么我們不需要反轉(zhuǎn)這里的順序來獲得同樣的期望結(jié)果呢?來自每個 reducer 的 combinationFn(..) 的抽象反轉(zhuǎn)了操作順序。所以和直覺相反,當(dāng)組合一個 tranducer 時,你只需要按照實際的順序組合就好!

    列表組合:純與不純

    我們再來看一下我們的 listCombination(..) 組合函數(shù)的實現(xiàn):

    function listCombination(list,val) {return list.concat( [val] ); }

    雖然這種方法是純的,但它對性能有負(fù)面影響。首先,它創(chuàng)建臨時數(shù)組來包裹 val。然后,concat(..) 方法創(chuàng)建一個全新的數(shù)組來連接這個臨時數(shù)組。每一步都會創(chuàng)建和銷毀的很多數(shù)組,這不僅對 CPU 不利,也會造成 GC 內(nèi)存的流失。

    下面是性能更好但是不純的版本:

    function listCombination(list,val) {list.push( val );return list; }

    單獨的考慮下 listCombination(..) ,毫無疑問,這是不純的,這通常是我們想要避免的。但是,我們應(yīng)該考慮一個更大的背景。

    listCombination(..) 不是我們完全有交互的函數(shù)。我們不直接在程序中的任何地方使用它,而只是在 transducing 的過程中使用它。

    回到第 5 章,我們定義純函數(shù)來減少副作用的目標(biāo)只是限制在應(yīng)用的 API 層級。對于底層實現(xiàn),只要沒有違反對外部是純函數(shù),就可以在函數(shù)內(nèi)為了性能而變得不純。

    listCombination(..) 更多的是轉(zhuǎn)換的內(nèi)部實現(xiàn)細節(jié)。實際上,它通常由 transducing 庫提供!而不是你的程序中進行交互的頂層方法。

    底線:我認(rèn)為甚至使用 listCombination(..) 的性能最優(yōu)但是不純的版本也是完全可以接受的。只要確保你用代碼注釋記錄下它不純即可!

    可選的組合

    到目前為止,這是我們用轉(zhuǎn)換所得到的:

    words .reduce( transducer( listCombination ), [] ) .reduce( strConcat, "" ); // 寫點什么

    這已經(jīng)非常棒了,但是我們還藏著最后一個的技巧。坦白來說,我認(rèn)為這部分能夠讓你迄今為止付出的所有努力變得值得。

    我們可以用某種方式實現(xiàn)只用一個 reduce(..) 來“組合”這兩個 reduce(..) 嗎? 不幸的是,我們并不能將 strConcat(..) 添加到 compose(..) 調(diào)用中; 它的“形狀”不適用于那個組合。

    但是讓我們來看下這兩個功能:

    function strConcat(str1,str2) { return str1 + str2; }function listCombination(list,val) { list.push( val ); return list; }

    如果你用心觀察,可以看出這兩個功能是如何互換的。它們以不同的數(shù)據(jù)類型運行,但在概念上它們也是一樣的:將兩個值組合成一個。

    換句話說, strConcat(..) 是一個組合函數(shù)!

    這意味著如果我們的最終目標(biāo)是獲得字符串連接而不是數(shù)組,我們就可以用它代替 listCombination(..) :

    words.reduce( transducer( strConcat ), "" ); // 寫點什么

    Boom! 這就是 transducing。

    最后

    深吸一口氣,確實有很多要消化。

    放空我們的大腦,讓我們把注意力轉(zhuǎn)移到如何在我們的程序中使用轉(zhuǎn)換,而不是關(guān)心它的工作原理。

    回想起我們之前定義的輔助函數(shù),為清楚起見,我們重新命名一下:

    var transduceMap = curry( function mapReducer(mapperFn,combinationFn){return function reducer(list,v){return combinationFn( list, mapperFn( v ) );}; } );var transduceFilter = curry( function filterReducer(predicateFn,combinationFn){return function reducer(list,v){if (predicateFn( v )) return combinationFn( list, v );return list;}; } );

    還記得我們這樣使用它們:

    var transducer = compose(transduceMap( strUppercase ),transduceFilter( isLongEnough ),transduceFilter( isShortEnough ) );

    transducer(..) 仍然需要一個組合函數(shù)(如 listCombination(..) 或 strConcat(..))來產(chǎn)生一個傳遞給 reduce(..) (連同初始值)的 transduce-reducer 函數(shù)。

    但是為了更好的表達所有這些轉(zhuǎn)換步驟,我們來做一個 transduce(..) 工具來為我們做這些步驟:

    function transduce(transducer,combinationFn,initialValue,list) {var reducer = transducer( combinationFn );return list.reduce( reducer, initialValue ); }

    這是我們的運行示例,梳理如下:

    var transducer = compose(transduceMap( strUppercase ),transduceFilter( isLongEnough ),transduceFilter( isShortEnough ) );transduce( transducer, listCombination, [], words ); // ["WRITTEN","SOMETHING"]transduce( transducer, strConcat, "", words ); // 寫點什么

    不錯,嗯! 看到 listCombination(..) 和 strConcat(..) 函數(shù)可以互換使用組合函數(shù)了嗎?

    Transducers.js

    最后,我們來說明我們運行的例子,使用sensors-js庫(https://github.com/cognitect-labs/transducers-js ):

    var transformer = transducers.comp(transducers.map( strUppercase ),transducers.filter( isLongEnough ),transducers.filter( isShortEnough ) );transducers.transduce( transformer, listCombination, [], words ); // ["WRITTEN","SOMETHING"]transducers.transduce( transformer, strConcat, "", words ); // WRITTENSOMETHING

    看起來幾乎與上述相同。

    注意: 上面的代碼段使用 transformers.comp(..) ,因為這個庫提供這個 API,但在這種情況下,我們從第 4 章的 compose(..) 也將產(chǎn)生相同的結(jié)果。換句話說,組合本身不是 transducing 敏感的操作。

    該片段中的組合函數(shù)被稱為 transformer ,而不是 transducer。那是因為如果我們直接調(diào)用 transformer(listCombination)(或 transformer(strConcat)),那么我們不會像以前那樣得到一個直觀的 transduce-reducer 函數(shù)。

    transducers.map(..) 和 transducers.filter(..) 是特殊的輔助函數(shù),可以將常規(guī)的斷言函數(shù)或映射函數(shù)轉(zhuǎn)換成適用于產(chǎn)生特殊變換對象的函數(shù)(里面包含了 reducer 函數(shù));這個庫使用這些變換對象進行轉(zhuǎn)換。此轉(zhuǎn)換對象抽象的額外功能超出了我們將要探索的內(nèi)容,請參閱該庫的文檔以獲取更多信息。

    由于 transformer(..) 產(chǎn)生一個變換對象,而不是一個典型的二元 transduce-reducer 函數(shù),該庫還提供 toFn(..) 來使變換對象適應(yīng)本地數(shù)組的 reduce(..) 方法:

    words.reduce(transducers.toFn( transformer, strConcat ),"" ); // WRITTENSOMETHING

    into(..) 是另一個提供的輔助函數(shù),它根據(jù)指定的空/初始值的類型自動選擇默認(rèn)的組合函數(shù):

    transducers.into( [], transformer, words ); // ["WRITTEN","SOMETHING"]transducers.into( "", transformer, words ); // WRITTENSOMETHING

    當(dāng)指定一個空數(shù)組 [] 時,內(nèi)部的 transduce(..) 使用一個默認(rèn)的函數(shù)實現(xiàn),這個函數(shù)就像我們的 listCombination(..)。但是當(dāng)指定一個空字符串 “” 時,會使用像我們的 strConcat(..) 這樣的方法。這很酷!

    如你所見,transducers-js 庫使轉(zhuǎn)換非常簡單。我們可以非常有效地利用這種技術(shù)的力量,而不至于陷入定義所有這些中間轉(zhuǎn)換器生產(chǎn)工具的繁瑣過程中去。

    總結(jié)

    Transduce 就是通過減少來轉(zhuǎn)換。更具體點,transduer 是可組合的 reducer。

    我們使用轉(zhuǎn)換來組合相鄰的map(..)、filter(..) 和 reduce(..) 操作。我們首先將 map(..) 和 filter(..) 表示為 reduce(..),然后抽象出常用的組合操作來創(chuàng)建一個容易組合的一致的 reducer 生成函數(shù)。

    transducing 主要提高性能,如果在延遲序列(異步 observables)中使用,則這一點尤為明顯。

    但是更廣泛地說,transducing 是我們針對那些不能被直接組合的函數(shù),使用的一種更具聲明式風(fēng)格的方法。否則這些函數(shù)將不能直接組合。如果使用這個技術(shù)能像使用本書中的所有其他技術(shù)一樣用的恰到好處,代碼就會顯得更清晰,更易讀! 使用 transducer 進行單次 reduce(..) 調(diào)用比追蹤多個 reduce(..) 調(diào)用更容易理解。

    ** 【上一章】翻譯連載 | 附錄 A:Transducing(上)-《JavaScript輕量級函數(shù)式編程》 |《你不知道的JS》姊妹篇 **

    iKcamp原創(chuàng)新書《移動Web前端高效開發(fā)實戰(zhàn)》已在亞馬遜、京東、當(dāng)當(dāng)開售。

    iKcamp官網(wǎng):https://www.ikcamp.com
    訪問官網(wǎng)更快閱讀全部免費分享課程:
    《iKcamp出品|全網(wǎng)最新|微信小程序|基于最新版1.0開發(fā)者工具之初中級培訓(xùn)教程分享》
    《iKcamp出品|基于Koa2搭建Node.js實戰(zhàn)項目教程》
    包含:文章、視頻、源代碼

    轉(zhuǎn)載于:https://www.cnblogs.com/ikcamp/p/7918852.html

    總結(jié)

    以上是生活随笔為你收集整理的翻译连载 | 附录 A:Transducing(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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