reduce與redux中compose函數(shù)

在說compose函數(shù)之前,我們先來看一道題目:

image.png

Your task is to write a higher order function for chaining together a list of unary functions. In other words, it should return a function that does a left fold on the given functions.

chained([a,b,c,d])(input)

Should yield the same result as

d(c(b(a(input))))

大致意思就是寫個(gè)函數(shù) 能將多個(gè)函數(shù)進(jìn)行組合成一個(gè)函數(shù),就是一元鏈?zhǔn)胶瘮?shù)

思考

當(dāng)時(shí)我想,要想實(shí)現(xiàn)一個(gè)這樣的函數(shù),肯定是需要有一個(gè)遍歷的過程,一個(gè)函數(shù)的執(zhí)行結(jié)果是另一個(gè)函數(shù)的參數(shù),那這樣就需要有個(gè)累計(jì)的過程,綜合以上2點(diǎn),想到數(shù)組中有個(gè)reduce函數(shù)。這里我們來看看reduce函數(shù):

arr.reduce(callback[, initialValue])

其中 callback是執(zhí)行數(shù)組中每個(gè)值的函數(shù),它包含四個(gè)參數(shù):

  • accumulator 累加器累加回調(diào)的返回值; 它是上一次調(diào)用回調(diào)時(shí)返回的累積值,或initialValue(如下所示)。

  • currentValue 數(shù)組中正在處理的元素。

  • currentIndex[可選] 數(shù)組中正在處理的當(dāng)前元素的索引。 如果提供了initialValue,則索引號(hào)為0,否則為索引為1。

  • array[可選] 調(diào)用reduce的數(shù)組

initialValue
[可選] 用作第一個(gè)調(diào)用 callback的第一個(gè)參數(shù)的值。 如果沒有提供初始值,則將使用數(shù)組中的第一個(gè)元素。 在沒有初始值的空數(shù)組上調(diào)用 reduce 將報(bào)錯(cuò)。

下面的例子求數(shù)組成員之和。

[1, 2, 3, 4, 5].reduce(function(x, y){
  console.log(x, y)
  return x + y;
});
// 1 2
// 3 3
// 6 4
// 10 5
//最后結(jié)果:15

上面代碼中,第一輪執(zhí)行,x是數(shù)組的第一個(gè)成員,y是數(shù)組的第二個(gè)成員。從第二輪開始,x為上一輪的返回值,y為當(dāng)前數(shù)組成員,直到遍歷完所有成員,返回最后一輪計(jì)算后的x。

利用reduce方法,可以寫一個(gè)數(shù)組求和的sum方法。

Array.prototype.sum = function (){
  return this.reduce(function (pre, next) {
    return pre + next;
  })
};

[3, 4, 5, 6, 10].sum()
// 28

如果要對(duì)累積變量指定初值,可以把它放在reduce方法的第二個(gè)參數(shù)。

[1, 2, 3, 4, 5].reduce(function(x, y){
  return x + y;
}, 10);
// 25

上面代碼指定參數(shù)x的初值為10,所以數(shù)組從10開始累加,最終結(jié)果為25。注意,這時(shí)y是從數(shù)組的第一個(gè)成員開始遍歷。

第二個(gè)參數(shù)相當(dāng)于設(shè)定了默認(rèn)值,處理空數(shù)組時(shí)尤其有用。

function add(prev, cur) {
  return prev + cur;
}

[].reduce(add)
// TypeError: Reduce of empty array with no initial value
[].reduce(add, 1)
// 1

上面代碼中,由于空數(shù)組取不到初始值,reduce方法會(huì)報(bào)錯(cuò)。這時(shí),加上第二個(gè)參數(shù),就能保證總是會(huì)返回一個(gè)值。

解決

在上面我們了解學(xué)習(xí)reduce之后,我們可以開始來解決這道題了,看代碼:

function chained(funcs) {
  return function(input){
    return funcs.reduce(function(input, fn){ return fn(input) }, input);
  }
}

驗(yàn)證一下

function f1(x){ return x*2 }
function f2(x){ return x+2 }
function f3(x){ return Math.pow(x,2) }

function f4(x){ return x.split("").concat().reverse().join("").split(" ")}
function f5(xs){ return xs.concat().reverse() }
function f6(xs){ return xs.join("_") }

Test.assertEquals( chained([f1,f2,f3])(0), 4 )
Test.assertEquals( chained([f1,f2,f3])(2), 36 )
Test.assertEquals( chained([f3,f2,f1])(2), 12 )

Test.assertEquals(chained([f4,f5,f6])("lorem ipsum"), "merol_muspi")
image.png

嗯,很好,驗(yàn)證通過的,沒什么問題!

進(jìn)一步思考

問題是解決了,但是認(rèn)真想想一下,這里的題目的需求是不是和redux中compose函數(shù)實(shí)現(xiàn)的需求一樣呢,嗯,我們來看看compose是怎么實(shí)現(xiàn)的。

https://github.com/reactjs/redux/blob/v3.7.2/src/compose.js

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

用es6寫的,關(guān)于es6的知識(shí),可以看 ECMAScript 6 入門或者es6的十大特性

參考這個(gè)compose函數(shù)的寫法,我們來解一下上面那道題,

function chained(...funcs) {
    if (funcs.length === 0) {
       return arg => arg
    }

    if (funcs.length === 1) {
      return funcs[0]
    }

    return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

驗(yàn)證一下:

function f1(x){ return x*2 }
function f2(x){ return x+2 }
function f3(x){ return Math.pow(x,2) }

function f4(x){ return x.split("").concat().reverse().join("").split(" ")}
function f5(xs){ return xs.concat().reverse() }
function f6(xs){ return xs.join("_") }

Test.assertEquals( chained(f1,f2,f3)(0), 4 )
Test.assertEquals( chained(f1,f2,f3)(2), 36 )
Test.assertEquals( chained(f3,f2,f1)(2), 12 )

Test.assertEquals(chained(f4,f5,f6)("lorem ipsum"), "merol_muspi")

image.png

waht? 怎么會(huì)只通過一個(gè),原來是順序反了,題目要求是從右到左累計(jì)的,所以這里我們就需要用到reduce函數(shù)的兄弟函數(shù)reduceRight了,關(guān)于兩者的區(qū)別,reduce是從左到右處理(從第一個(gè)成員到最后一個(gè)成員),reduceRight則是從右到左(從最后一個(gè)成員到第一個(gè)成員),其他完全一樣。

最終代碼:

function chained(...funcs) {
    if (funcs.length === 0) {
       return arg => arg
    }

    if (funcs.length === 1) {
      return funcs[0]
    }

    return funcs.reduceRight((a, b) => (...args) => a(b(...args)))
}

或者

function chained(...funcs) {
    if (funcs.length === 0) {
       return arg => arg
    }

    if (funcs.length === 1) {
      return funcs[0]
    }

    return funcs.reduce((a, b) => (...args) => b(a(...args)))
}

嗯,完美。

參考:
Array.prototype.reduce()

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 感謝社區(qū)中各位的大力支持,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠,并抽取幸運(yùn)大獎(jiǎng):點(diǎn)擊這里領(lǐng)取 你...
    HetfieldJoe閱讀 831評(píng)論 0 2
  • php usleep() 函數(shù)延遲代碼執(zhí)行若干微秒。 unpack() 函數(shù)從二進(jìn)制字符串對(duì)數(shù)據(jù)進(jìn)行解包。 uni...
    思?jí)鬚HP閱讀 2,133評(píng)論 1 24
  • 感謝社區(qū)中各位的大力支持,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠,并抽取幸運(yùn)大獎(jiǎng):點(diǎn)擊這里領(lǐng)取 至...
    HetfieldJoe閱讀 952評(píng)論 0 2
  • 五月天好好(想把你寫成一首歌)歌詞 想把你寫成一首歌 想養(yǎng)一只貓 想要回到每個(gè)場景 撥慢每只表 我們?cè)谛『⒑痛笕说?..
    南貓隨筆閱讀 226評(píng)論 0 0
  • 本文主要是對(duì)Hysys 8.8/9.0 的新版說明文檔中Recycle模塊的翻譯,加入了一些個(gè)人理解。本文的內(nèi)容僅...
    Meatle閱讀 4,123評(píng)論 1 5

友情鏈接更多精彩內(nèi)容