3.Reduce 函數(shù)揭秘
Topic 3:
請用Reduce函數(shù)對Int類型數(shù)組內(nèi)所有元素求和,例如數(shù)組[1,2,3,4]的4個(gè)元素和為10.
Example:
//例一:
let intArray = [1,2,3,4]
var sum = intArray.reduce(0){
result, x in
result + x
}
why coding like this?
1.開篇
命題一:假設(shè)讓你寫一個(gè)函數(shù)來實(shí)現(xiàn)對Int類型數(shù)組內(nèi)所有元素求和,例如數(shù)組[1,2,3,4]的求和結(jié)果為10。
思路:與類map,filter函數(shù)思路不同,前者是對數(shù)組內(nèi)每一個(gè)元素單獨(dú)做處理,而reduce恰恰相反,是將所有數(shù)組元素整合到一起。因此關(guān)于求和函數(shù)思路很明確,遍歷整個(gè)數(shù)組元素,逐一求和后作為結(jié)果值返回,大致為 0 + 1 + 2 + 3 +4。其中0是初始值。
代碼:
//例二:
func sum(xs:[Int])->Int{
var result:Int = 0
for x in xs{
result += x
}
return result
}
//不妨測試下
sum(intArray)//print 10
可以看到對于sum函數(shù)傳入Int類型數(shù)組,首先我們聲明一個(gè)Int類型變量用于保存最后的求和結(jié)果值,初始值為0;接著使用for-in語句遍歷傳入的數(shù)組,使用result += x逐一求和累加;最后返回結(jié)果值result。
2.過渡
命題二:基礎(chǔ)入門之后,按照慣例對條件進(jìn)行改變,假如把所有元素求和更改為對所有元素求乘,如[1,2,3,4] 返回1 * 2 * 3 * 4 = 24。
思路:顯然對于前面的代碼稍許改動即可,主要是替換result += x(累加運(yùn)算) 更為result = result * x(階乘運(yùn)算)。當(dāng)然有一點(diǎn)切記不要遺忘,就是初始值 result = 1。
代碼:
//例三:
func multiplicator(xs:[Int])->Int{
var result:Int = 1
for x in xs{
result = x * result
}
return result
}
multiplicator(intArray)//輸出24
命題三: 說完Int類型數(shù)組,再來說說String類型,將一個(gè)文字片段數(shù)組組成一個(gè)完整的句子,例如["hello"," ","world","!","say"," ","by"," ","optionalswift"],最后整合成"hello world!say by optionalswift"。
思路: 思路大概是遍歷數(shù)組,然后將所有字符串元素拼接在一起,關(guān)鍵這個(gè)拼接用什么?例如有str1和str2,swift中其實(shí)只需要簡單的newStr = str1 + str2 即可。
代碼:
//例四:
func jointStringArray(xs:[String])->String{
var result :String = ""
for x in xs{
result += x
}
return result
}
let helloworld = ["hello"," ","world","!","say"," ","by"," ","optionalswift"]
jointStringArray(helloworld)//輸出"hello world!say by optionalswift"
你已經(jīng)急著上手想寫泛型函數(shù)了嗎?等等,我們貌似還忽略了一些復(fù)雜的鏈接情況。比如["hello","world","say","By","optional"],我們需要在每一個(gè)單詞之間插入"-"連字符,別問我為什么?因?yàn)槭俏页鲱}!
//例五:hyphen 意思為連字符`-`
func jointStringArrayByHyphen(xs:[String])->String{
var result : String = "整合后的字符串為:"
for x in xs{
result = result + "-" + x //注意右側(cè)是一個(gè)類似combine(result,x)函數(shù)進(jìn)行處理 得到整合后的結(jié)果給result
}
return result
}
let newArray = ["hello","world","say","By","optional"]
jointStringArrayByHyphen(newArray)//輸出"整合后的字符串為:-hello-world-say-By-optional"
3.高潮
在開始寫我們的泛型reduce函數(shù)之前,分析函數(shù)的輸入輸出以及如何實(shí)現(xiàn):首先該函數(shù)將傳入一個(gè)泛型類數(shù)組,暫且定為[T];此外函數(shù)可自己設(shè)定初始值給result,暫且定為U;最后是重中之重:傳入一個(gè)閉包作為連接result和數(shù)組元素的處理函數(shù),combine(result,x),不難分析result的類型為T,x是數(shù)組xs中的元素,類型為U,返回嘛自然是和result一致的類型嘍。因此combine閉包類型為(U,T)->U。因此代碼這么寫:
//例六:
func myReduce<T,U>(arr:[T],initialValue:U,combine:(U,T)->U)->U{
var result = initialValue //賦值初始值 類型為U 并且是作為結(jié)果值返回的
for elem in arr{
result = combine(result,elem) //注意右側(cè)是傳入的閉包 該閉包類型為(U,T)->U,即把上一次的結(jié)果值依次和數(shù)組元素做拼接處理,該處理可在閉包中實(shí)現(xiàn),取決于你
}
return result
}
myReduce(newArray, "整合后的字符串為:"){
result , elem in //注意result ,x 于combine(result,elem)相對應(yīng)
return result + "-" + x //注意這里return 其實(shí)是可以省略的!
}
不得不說,這個(gè)函數(shù)還是有點(diǎn)料的,需要細(xì)細(xì)品味一番。你以為這就結(jié)束了嗎,還有落幕呢?
4.落幕
對sum函數(shù)進(jìn)行改寫,使用前面自定義的myReduce函數(shù)封裝
func sumUsingReduce(xs:[Int])->Int{
return myReduce(xs,0){result, x in result + x}
}
注意到省略了return 因?yàn)閟wift會幫你推算要返回什么。簡化的感覺不夠徹底。
func sumUsingReduce(xs:[Int])->Int{
return myReduce(xs,0,+)
}
閉包僅僅傳入了一個(gè)+號,swift推算過程是首先combine閉包有兩個(gè)傳入?yún)?shù)result和elem,除此之外別無其他,因此+只能對這兩個(gè)參數(shù)求和,得到一個(gè)結(jié)果值x,由于combine函數(shù)還需要返回一個(gè)結(jié)果值,但是思來想去貌似除了x沒有其他可用,因此把x作為閉包結(jié)果值返回和result相加。
類似的你可是使用return myReduce(xs,1,*)。
你以為這就結(jié)束了嗎? 現(xiàn)在用reduce來改寫map函數(shù) 以及filter函數(shù)
func mapUsingReduce<T,U>(xs:[T],f:T->U)->[U]{
return xs.reduce([]){result,x in result + [f(x)]}//使用了系統(tǒng)API 嘗試用自定義的
}
首先注意到函數(shù)傳入的兩個(gè)參數(shù)以及返回結(jié)果值和早前map函數(shù)是一模一樣的,關(guān)鍵是在主體的實(shí)現(xiàn)上!
再來看filter的實(shí)現(xiàn):
filterUsingReduce<T>(xs:[T],check:T->Bool)->[T]{
return xs.reduce([]){
return check(x) ? result + [x] : result //使用了系統(tǒng)API 嘗試用自定義的
}
}
map,filter,reduce 小節(jié)至此結(jié)束! 希望對大家有收獲!