2011年5月1日 星期日

4. Loop、Iteration 與 Recursivion

有許多朋友問道,如何寫好「像」函數式的程式。
這裡先澄清,函數式的程式不一定比較好,但寫作完畢函數式的品質通常比較好(比較不會出錯),可是函數式的效率通常比命令式的程式差。

在 Scala 中,兩種方式都有支援,如何取捨端看各人。

Loop 是非常常見的東西,我們從 Loop 來看如何寫「像」函數式的程式。
撰寫重複工作的程式有幾種技巧
1. 使用 Loop
2. 使用 Iteration
3. 使用 Recursion

假設現在我們要解決一個簡單的問題,想要找出一群人中,所有男生的總共歲數。
我們先將需要的資料定義出來
1. class People
2. lst:本範例放入 4 個人
class People(val name: String, val age: Int, val gender: String) {
  def isMale() = gender == "M"
}

val lst = List(new People ("John", 10, "M"), 
               new People("Steven", 20, "M"),
               new People("Kelly", 15, "F"),
               new People("Jeniffer", 18, "F"))

各解法的方式如下:

1. 使用 Loop:直接對每一個people做檢查
def average1(lst: List[People]) = {
    var totalAge = 0
    for (p <- lst) {
        if (p.isMale) {
            totalAge += p.age
        }
    }
    totalAge
}
2. 使用 Iteration:先使用 filter,然後直接運算
def average2(lst: List[People]) = lst.filter(_.isMale).foldLeft(0)((total, people) => total + people.age)
3. 使用 Recursion
def average3(lst: List[People]):Int = lst match {
    case Nil => 0
    case p :: ps => if (p.isMale) p.age + average3(ps) else average3(ps)
}

上述三種作法都可以達到功能。在這三種作法中,看出函數式作法的威力了?

Loop 的作法比較命令式,一個個 element 檢查,若符合則進行下一個 statement。命令式的邏輯是資料讀取、檢查、往下一個 statement以及修改的方式。

Iteration 的作法就比較使用Higher Order Function的作法,完全運行在 List 上,先將不要的 element 剃除掉,然後對整個符合的 list 做一次 foldLeft 運算。原先的問題,轉為「剃除」與「求整體值」,簡單、且清楚。

Recursion 的作法透過一個檢查條件,分為空 List 與非空 List。空 List 當然總共歲數為 0,非空 List則是第一個 element 的歲數,加上剩下的總共歲數。這裡所展現的是非常標準的 List 操作手法,應該也很清楚。

三種作法,你喜歡哪一種? Iteration 與 Recursion 都廣泛運用在 Scala 程式中,建議你要熟悉這些手法。

沒有留言:

張貼留言