1. 程式人生 > 程式設計 >詳解JS中的reduce fold unfold用法

詳解JS中的reduce fold unfold用法

fold(reduce)

說說reduce吧,很喜歡這個函式,節省了不少程式碼量,而且有一些宣告式的雛形了,一些常見的工具函式,flatten,deepCopy,mergeDeep等用reduce實現的很優雅簡潔。reduce也稱為fold,本質上就是一個摺疊陣列的過程,把陣列中的多個值經過運算變成一個值,每次運算都會有一個函式處理,這個函式就是reduce的核心元素,稱之為reducer,reducer函式是個2元函式,返回1個單值,常見的add函式就是reducer

const addReducer = (x,y) => x + y;

這個add函程式設計客棧數就是一個reducer,最常見的用法就是結合陣列的reduce方法來用

[1,2,3,4,5].reduce(addReducer,0) // 15

為了更好的理解reduce,下面用不同的思路實現一遍這個函式

使用for...o程式設計客棧f

const reduce = (f,init,arr) => {
  let acc = init;
  for (const item of arr) {
    acc = f(acc,item);
  }
  return acc
}
// 執行
reduceFor(addReducer,[1,5])  // 15

使用while迴圈

reduce = (f,arr) => {
  let acc = init;
  let current;
  let i = 0;
  while ((current = arr[i++])) {
    acc = f(acc,current);
  }
  return acc;
}

// 執行
reduceFor(addReducer,5])  // 15

更像fold的實現

上面的實現也都好理解,但好像沒有體現出來摺疊(fold)這個過程,摺疊應該是對陣列的層層擠壓操作,上面的實現陣列和邏輯其實是分開了,而且也引入了比較多的中間變數,雖然是在內部沒有副作用吧。
其實換個思路想一下,如果把狀態通過引數來傳遞,就可以更好的體現fold的過程,這裡的引數是值是指逐漸變化的陣列和計算值,並可以儘可能的做到無狀態,真正純函式的實現是沒有表示式,只有語句的,這個可以用遞迴做到。下面的實現是用尾遞迴實現的reduce,可以在實現的過程中就看出陣列和計算值是怎樣變化的。非常符合fold這個稱謂

function reduce(f,in程式設計客棧it,arr) {
  if (arr.length === 0) return init;
  const [head,...rest] = arr;
  return reduceRecursion(f,f(init,head),rest);
}

// 執行
reduceFor(addReducer,5])  // 15

unfold

fold反過來就是unfold,unfold顧名思義就是根據一個反過來的reducer,來生成一系列的值。此時這個如果說原來的reducer實現類似於(a,b) -> c,那反過來就是c -> [a,b],生成序列是一個很基本的操作,但就是這個基本的操作,也有很多實現的思路,在介紹unfold之前,看一下實程式設計客棧現序列的其他方法,最後來做一個對比。

序列的實現

range(0,100,5)

期待結果

[0,5,10,... 95]

陣列實現

這個就不多說了,大家應該都知道。

range = (first,last,step) => {
  const n = (last - first) / step + 1;
  return Array.from({ length: n - 1 })
            .map((_,index)程式設計客棧 => first + index * step);
}
// 也可以使用from的第二個引數
// Array.from({ length: n },(_,i) => first + i * step);

生成器實現

生成序列還有一個利器,那就是generator,生成器生成器,就是用來生成資料的。generator返回一個迭代器,也很容易生成序列

function* range(first,step) {
  let acc = first;
  while (acc < last) {
    yield acc;
    acc = acc + step;
  }
}
[...range(0,5)]

兩者相比,generator更注重生成的過程,Array注重資料變化的過程。

unfold實現

在實現unfold之前,首先梳理一下實現思路,和fold一樣,也是用遞迴,且要在實現的過程中看到對應資料的變化。大體過程如下

0 -> [0,5]

5 -> [5,10]

10 -> [10,15]

15 -> [15,20]

...

90 -> [90,95]

95 -> [95,100]

可以看出過程恰恰是fold反過來,符合c -> [a,b]因為初始值肯定為一個數組,所以unfold只需要兩個引數,實現如下。

function unfold(f,init) {
  const g = (f,next,acc) => {
    const result = f(next);
    const [head,last] = result || [];
    console.log(last);
    return result ? g(f,acc.concat(head)) : acc;
  };
  return g(f,[]);
}

range = R.curry((first,step) =>
  unfold(next => next < last && [next,next + step],0)
)

// 執行
range(0,5)

總結

以上就是結合reduce和一個生成序列的例子簡單介紹了一下fold和unfold這兩個在fp程式設計中很重要的概念,當然他們功能不只是生成序列,還有很多很強大的功能

以上就是詳解js中的reduce fold unfold用法的詳細內容,更多關於JS的資料請關注我們其它相關文章!