Why Coding Like this -------Reduce揭祕

title: “Why coding like This —— Reduce 函數揭祕”
date: 2015-08-03 19:24:16
categories: “why coding like this”
tags: [swift進階]


3.Reduce 函數揭祕

Topic 3:

請用Reduce函數對Int類型數組內所有元素求和,例如數組[1,2,3,4]的4個元素和爲10.

Example:

//例一:
let intArray = [1,2,3,4]

var sum = intArray.reduce(0){
  result, x in
  result + x
}

why coding like this?

1.開篇

命題一:假設讓你寫一個函數來實現對Int類型數組內所有元素求和,例如數組[1,2,3,4]的求和結果爲10。
思路:與類map,filter函數思路不同,前者是對數組內每一個元素單獨做處理,而reduce恰恰相反,是將所有數組元素整合到一起。因此關於求和函數思路很明確,遍歷整個數組元素,逐一求和後作爲結果值返回,大致爲 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函數傳入Int類型數組,首先我們聲明一個Int類型變量用於保存最後的求和結果值,初始值爲0;接着使用for-in語句遍歷傳入的數組,使用result += x逐一求和累加;最後返回結果值result。

2.過渡

命題二:基礎入門之後,按照慣例對條件進行改變,假如把所有元素求和更改爲對所有元素求乘,如[1,2,3,4] 返回1 * 2 * 3 * 4 = 24。
思路:顯然對於前面的代碼稍許改動即可,主要是替換result += x(累加運算) 更爲result = result * x(階乘運算)。當然有一點切記不要遺忘,就是初始result = 1
代碼:

//例三:
func multiplicator(xs:[Int])->Int{
    var result:Int = 1
    for x in xs{
        result = x * result
    }
    return result
}
multiplicator(intArray)//輸出24

命題三: 說完Int類型數組,再來說說String類型,將一個文字片段數組組成一個完整的句子,例如["hello"," ","world","!","say"," ","by"," ","optionalswift"],最後整合成“hello world!say by optionalswift”
思路: 思路大概是遍歷數組,然後將所有字符串元素拼接在一起,關鍵這個拼接用什麼?例如有str1str2,swift中其實只需要簡單的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"  

你已經急着上手想寫泛型函數了嗎?等等,我們貌似還忽略了一些複雜的鏈接情況。比如[“hello”,”world”,”say”,”By”,”optional”],我們需要在每一個單詞之間插入”-“連字符,別問我爲什麼?因爲是我出題!

//例五:hyphen 意思爲連字符`-`
func jointStringArrayByHyphen(xs:[String])->String{
  var result : String = "整合後的字符串爲:"
  for x in xs{
    result = result + "-" + x  //注意右側是一個類似combine(result,x)函數進行處理 得到整合後的結果給result
  }
  return result
}
let newArray = ["hello","world","say","By","optional"]
jointStringArrayByHyphen(newArray)//輸出"整合後的字符串爲:-hello-world-say-By-optional"

3.高潮

在開始寫我們的泛型reduce函數之前,分析函數的輸入輸出以及如何實現:首先該函數將傳入一個泛型類數組,暫且定爲[T];此外函數可自己設定初始值給result,暫且定爲U;最後是重中之重:傳入一個閉包作爲連接result和數組元素的處理函數,combine(result,x),不難分析result的類型爲T,x是數組xs中的元素,類型爲U,返回嘛自然是和result一致的類型嘍。因此combine閉包類型爲(U,T)->U。因此代碼這麼寫:

//例六:
func myReduce<T,U>(arr:[T],initialValue:U,combine:(U,T)->U)->U{
  var result = initialValue //賦值初始值 類型爲U 並且是作爲結果值返回的
  for elem in arr{
    result = combine(result,elem)   //注意右側是傳入的閉包 該閉包類型爲(U,T)->U,即把上一次的結果值依次和數組元素做拼接處理,該處理可在閉包中實現,取決於你
  }
  return result
}
myReduce(newArray, "整合後的字符串爲:"){
  result , elem in //注意result ,x 於combine(result,elem)相對應
  return result + "-" + x //注意這裏return 其實是可以省略的!
}

不得不說,這個函數還是有點料的,需要細細品味一番。你以爲這就結束了嗎,還有落幕呢?

4.落幕

對sum函數進行改寫,使用前面自定義的myReduce函數封裝

func sumUsingReduce(xs:[Int])->Int{
    return myReduce(xs,0){result, x in result + x}
}

注意到省略了return 因爲swift會幫你推算要返回什麼。簡化的感覺不夠徹底。

func sumUsingReduce(xs:[Int])->Int{
    return myReduce(xs,0,+)
}

閉包僅僅傳入了一個+號,swift推算過程是首先combine閉包有兩個傳入參數result和elem,除此之外別無其他,因此+只能對這兩個參數求和,得到一個結果值x,由於combine函數還需要返回一個結果值,但是思來想去貌似除了x沒有其他可用,因此把x作爲閉包結果值返回和result相加。

類似的你可是使用return myReduce(xs,1,*)。

你以爲這就結束了嗎? 現在用reduce來改寫map函數 以及filter函數

func mapUsingReduce<T,U>(xs:[T],f:T->U)->[U]{
    return xs.reduce([]){result,x in result + [f(x)]}//使用了系統API 嘗試用自定義的
}

首先注意到函數傳入的兩個參數以及返回結果值和早前map函數是一模一樣的,關鍵是在主體的實現上!設定初始值爲[]等價於var result = [],而閉包的內容result ,x in result + [f(x)] 又等價於遍歷數組時執行的語句result.apped(f(x))。請仔細思考下!小技巧 reduce的閉包內容就是等式result = combine(result,x) 的combine函數,意味着每次調用閉包返回一個處理過後的值給result即可,而result又是函數內私有變量,只有全部調用後才返回。

再來看filter的實現:

filterUsingReduce<T>(xs:[T],check:T->Bool)->[T]{
    return xs.reduce([]){
        return check(x) ? result + [x] : result //使用了系統API 嘗試用自定義的
    }
}

map,filter,reduce 小節至此結束! 希望對大家有收穫!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章