詳解JS中的reduce fold unfold用法
fold(reduce)
說說reduce吧, 很喜歡這個函數(shù),節(jié)省了不少代碼量,而且有一些聲明式的雛形了,一些常見的工具函數(shù),flatten,deepCopy,mergeDeep等用reduce實現(xiàn)的很優(yōu)雅簡潔。reduce也稱為fold,本質(zhì)上就是一個折疊數(shù)組的過程,把數(shù)組中的多個值經(jīng)過運算變成一個值,每次運算都會有一個函數(shù)處理,這個函數(shù)就是reduce的核心元素,稱之為reducer,reducer函數(shù)是個2元函數(shù),返回1個單值,常見的add函數(shù)就是reducer
const addReducer = (x, y) => x + y;
這個add函數(shù)就是一個reducer,最常見的用法就是結(jié)合數(shù)組的reduce方法來用
[1, 2, 3, 4, 5].reduce(addReducer, 0) // 15
為了更好的理解reduce,下面用不同的思路實現(xiàn)一遍這個函數(shù)
使用for...of
const reduce = (f, init, arr) => {
let acc = init;
for (const item of arr) {
acc = f(acc, item);
}
return acc
}
// 執(zhí)行
reduceFor(addReducer, 0, [1, 2, 3, 4, 5]) // 15
使用while循環(huán)
reduce = (f, init, arr) => {
let acc = init;
let current;
let i = 0;
while ((current = arr[i++])) {
acc = f(acc, current);
}
return acc;
}
// 執(zhí)行
reduceFor(addReducer, 0, [1, 2, 3, 4, 5]) // 15
更像fold的實現(xiàn)
上面的實現(xiàn)也都好理解,但好像沒有體現(xiàn)出來折疊(fold)這個過程,折疊應該是對數(shù)組的層層擠壓操作,上面的實現(xiàn)數(shù)組和邏輯其實是分開了,而且也引入了比較多的中間變量,雖然是在內(nèi)部沒有副作用吧。
其實換個思路想一下,如果把狀態(tài)通過參數(shù)來傳遞,就可以更好的體現(xiàn)fold的過程,這里的參數(shù)是值是指逐漸變化的數(shù)組和計算值,并可以盡可能的做到無狀態(tài),真正純函數(shù)的實現(xiàn)是沒有表達式,只有語句的,這個可以用遞歸做到。下面的實現(xiàn)是用尾遞歸實現(xiàn)的reduce,可以在實現(xiàn)的過程中就看出數(shù)組和計算值是怎樣變化的。非常符合fold這個稱謂
function reduce(f, init, arr) {
if (arr.length === 0) return init;
const [head, ...rest] = arr;
return reduceRecursion(f, f(init, head), rest);
}
// 執(zhí)行
reduceFor(addReducer, 0, [1, 2, 3, 4, 5]) // 15
unfold
fold反過來就是unfold,unfold顧名思義就是根據(jù)一個反過來的reducer,來生成一系列的值。此時這個如果說原來的reducer實現(xiàn)類似于(a, b) -> c,那反過來就是c -> [a, b], 生成序列是一個很基本的操作,但就是這個基本的操作,也有很多實現(xiàn)的思路,在介紹unfold之前,看一下實現(xiàn)序列的其他方法,最后來做一個對比。
序列的實現(xiàn)
range(0, 100, 5)
期待結(jié)果
[0, 5, 10, ... 95]
數(shù)組實現(xiàn)
這個就不多說了,大家應該都知道。
range = (first, last, step) => {
const n = (last - first) / step + 1;
return Array.from({ length: n - 1 })
.map((_, index) => first + index * step);
}
// 也可以使用from的第二個參數(shù)
// Array.from({ length: n }, (_, i) => first + i * step);
生成器實現(xiàn)
生成序列還有一個利器,那就是generator,生成器生成器,就是用來生成數(shù)據(jù)的。generator返回一個迭代器,也很容易生成序列
function* range(first, last, step) {
let acc = first;
while (acc < last) {
yield acc;
acc = acc + step;
}
}
[...range(0, 100, 5)]
兩者相比,generator更注重生成的過程,Array注重數(shù)據(jù)變化的過程。
unfold實現(xiàn)
在實現(xiàn)unfold之前,首先梳理一下實現(xiàn)思路,和fold一樣,也是用遞歸,且要在實現(xiàn)的過程中看到對應數(shù)據(jù)的變化。大體過程如下
0 -> [0, 5]
5 -> [5, 10]
10 -> [10, 15]
15 -> [15, 20]
...
90 -> [90, 95]
95 -> [95, 100]
可以看出過程恰恰是fold反過來,符合c -> [a, b]因為初始值肯定為一個數(shù)組,所以unfold只需要兩個參數(shù),實現(xiàn)如下。
function unfold(f, init) {
const g = (f, next, acc) => {
const result = f(next);
const [head, last] = result || [];
console.log(last);
return result ? g(f, last, acc.concat(head)) : acc;
};
return g(f, init, []);
}
range = R.curry((first, last, step) =>
unfold(next => next < last && [next, next + step], 0)
)
// 執(zhí)行
range(0, 100, 5)
總結(jié)
以上就是結(jié)合reduce和一個生成序列的例子簡單介紹了一下fold和unfold這兩個在fp編程中很重要的概念,當然他們功能不只是生成序列,還有很多很強大的功能
以上就是詳解JS中的reduce fold unfold用法的詳細內(nèi)容,更多關(guān)于JS的資料請關(guān)注腳本之家其它相關(guān)文章!
- JS中的reduce()方法使用小結(jié)
- js利用reduce方法讓你的代碼更加優(yōu)雅
- JavaScript數(shù)組reduce()方法的語法與實例解析
- javascript數(shù)組includes、reduce的基本使用
- js 數(shù)組 find,some,filter,reduce區(qū)別詳解
- JS數(shù)組reduce你不得不知道的25個高級用法
- JS使用reduce()方法處理樹形結(jié)構(gòu)數(shù)據(jù)
- JavaScript Reduce使用詳解
- 詳解JavaScript之Array.reduce源碼解讀
- 8個JS的reduce使用實例和reduce操作方式
相關(guān)文章
前端項目中報錯Uncaught?(in?promise)的解決方法
最近在做項目的時候控制臺報了一個錯Uncaught(in promise) false,這篇文章主要給大家介紹了關(guān)于前端項目中報錯Uncaught?(in?promise)的解決方法,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-04-04
js實現(xiàn)3D粒子酷炫動態(tài)旋轉(zhuǎn)特效
這篇文章主要為大家詳細介紹了js實現(xiàn)3D粒子酷炫動態(tài)旋轉(zhuǎn)特效,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-09-09
使用JavaScript為Kindeditor自定義按鈕增加Audio標簽
這篇文章主要介紹了使用JavaScript為Kindeditor自定義按鈕增加Audio標簽的方法,KindEditor是一套開源的HTML可視化編輯器,需要的朋友可以參考下2016-03-03
JavaScript自定義等待wait函數(shù)實例分析
這篇文章主要介紹了JavaScript自定義等待wait函數(shù),實例分析了自定義等待函數(shù)的實現(xiàn)與使用技巧,需要的朋友可以參考下2015-03-03
javascript eval和JSON之間的聯(lián)系
本文著重解釋eval函數(shù)和JSON數(shù)據(jù)格式之間的聯(lián)系以及一些細節(jié)上的問題。2009-12-12

