1. fold
介紹
從本質上說,fold
函數將一種格式的輸入數據轉化成另外一種格式返回。fold
, foldLeft
和foldRight
這三個函數除了有一點點不同外,做的事情差不多。我將在下文解釋它們的共同點並解釋它們的不同點。
我將從一個簡單的例子開始,用fold計算一系列整型的和。
val numbers = List(5, 4, 8, 6, 2)
numbers.fold(0) { (z, i) =>
z + i
}
// result = 25
List
中的fold
方法需要輸入兩個參數:初始值以及一個函數。輸入的函數也需要輸入兩個參數:累加值和當前item的索引。那麼上面的代碼片段發生了什麼事?
- 代碼開始運行的時候,初始值0作爲第一個參數傳進到
fold
函數中,list中的第一個item作爲第二個參數傳進fold
函數中。 fold
函數開始對傳進的兩個參數進行計算,在本例中,僅僅是做加法計算,然後返回計算的值;Fold
函數然後將上一步返回的值作爲輸入函數的第一個參數,並且把list中的下一個item作爲第二個參數傳進繼續計算,同樣返回計算的值;- 第2步將重複計算,直到list中的所有元素都被遍歷之後,返回最後的計算值,整個過程結束;
- 這雖然是一個簡單的例子,讓我們來看看一些比較有用的東西。早在後面將會介紹
foldleft
函數,並解釋它和fold
之間的區別,目前,你只需要想象foldLeft
函數和fold
函數運行過程一樣。
2. foldLeft
介紹
下面是一個簡單的類和伴生類:
class Foo(val name: string, val age: int, val sex: symbol)
object Foo {
def apply(name: string, age: int, sex: symbol) = new foo(name, age, sex)
}
假如我們有很多的Foo實例,並存在list中:
val fooList = foo("hugh jass", 25, 'male) ::
foo("biggus dickus", 43, 'male) ::
foo("incontinentia buttocks", 37, 'female) ::
nil
我們想將上面的list
轉換成一個存儲[title] [name], [age]格式的string鏈表:
val stringlist = foolist.foldleft(list[string]()) { (z, f) =>
val title = f.sex match {
case 'male => "mr."
case 'female => "ms."
}
z :+ s"$title ${f.name}, ${f.age}"
}
// stringList(0)
// Mr. Hugh jass, 25
// stringList(2)
// Ms. Incontinentia buttocks, 37
和第一個例子一樣,我們也有個初始值,這裏是一個空的string list,也有一個操作函數。在本例中,我們判斷了性別,並構造了我們想要的string,並追加到累加器中(這裏是一個list)。
3. fold, foldleft, and foldright之間的區別
主要的區別是操作函數遍歷問題集合的順序。foldleft
是從左開始計算,然後往右遍歷。foldright
是從右開始算,然後往左遍歷。而fold
遍歷的順序沒有特殊的次序。來看下這三個函數的實現吧(在traversableonce
特質裏面實現)
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
def foldLeft[B](z: B)(op: (B, A) => B): B = {
var result = z
this.seq foreach (x => result = op(result, x))
result
}
def foldRight[B](z: B)(op: (A, B) => B): B =
reversed.foldLeft(z)((x, y) => op(y, x))
由於fold
函數遍歷沒有特殊的次序,所以對fold
的初始化參數和返回值都有限制。在這三個函數中,初始化參數和返回值的參數類型必須相同。
第一個限制是初始值的類型必須是list中元素類型的超類。在我們的例子中,我們的對List[Int]進行fold
計算,而初始值是Int類型的,它是List[Int]的超類。
第二個限制是初始值必須是中立的(neutral)。也就是它不能改變結果。比如對加法來說,中立的值是0;而對於乘法來說則是1,對於list來說則是Nil。
順便說下,其實foldLeft和foldRight函數還有兩個縮寫的函數:
def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op)
def :\[B](z: B)(op: (A, B) => B): B = foldRight(z)(op)
scala> (0/:(1 to 100))(_+_)
res32: Int = 5050
scala> ((1 to 100):\0)(_+_)
res24: Int = 5050
【完】