首先咱們從scala的函數開始:javascript
在命令後輸入:(x:Int) => x * 2java
這種奇怪的格式讓咱們陌生,可是若是你熟悉javascript的函數以下:編程
function myfunc(param){數組
alert("hello" + param);函數
}spa
這是一個彈出窗口hello的函數,顯示的是hellp+輸入參數,這個param不僅是能夠傳入值,也能夠傳入另一個函數,爲了可以傳入另一個函數做爲參數,被傳入的函數在寫法上要改變一下,好比:scala
var myfunc2 = function (param){code
alert("hello" + param);orm
}htm
能夠看到,咱們將函數名稱移到了左邊,右邊就剩餘function和()以及{}三個符號,這樣咱們能夠傳入myfunc了:
myfunc(myfunc2);
咱們已經理解了JS中函數傳遞,那麼Scala中也是相似,上面的(x:Int) => x * 2 其實能夠當作JS的(x:Int) { x * 2 } ,咱們使用大括號替代右箭頭=>,二者意思差很少(少一個function付)。等同於js:
var myfunc3 = function (x) {
return x * 2 ;
}
scala的x:Int相似Java的Int x,Int是x的類型,js是動態語言,因此類型定義是不須要的。Scala的寫法是:
var myfunc = (x:Int) => x * 2
咱們本身簡寫成(x:Int) => x * 2也能夠,和myfunc同樣處處引用使用,沒有名稱而已,也就是匿名函數,能夠做爲另一個函數的輸入參數使用。如:
myfunc2( (x:Int) => x * 2);
myfunc這個函數本身也能夠被直接使用:
myfunc(2)
結果是4;
那麼myfunc(myfunc(2))是多少呢,注意,這裏不是myfunc2,而是myfunc本身。
myfunc(2)
結果是8; 至關於調用了兩次本身。
面向函數編程常常形象的比喻成相似集成電路的輸入輸出同樣:
輸入--->函數運算 -->輸出
因此,這裏(x:Int) => x * 2也有這三種結構:
輸入x---->函數運算x * 2 ---->輸出x*2的結果。
輸出x*2結果和x*2運算實際是捆綁在一塊兒,是一體的,因此,通常咱們就不顯式象js中聲明return x*2。Scala的"=>"符號的 右邊能夠認爲表明細節,表明函數體,表明ReturnValue is 「右邊」.
有了前面熱身,咱們對函數是第一等公民有個初步印象,下面再看看函數如何做爲值傳遞的:
val myList = List(1,2,3,4,5)
for(x:Int <- myList) yield myfunc (x)
yield是專門用於for循環,將新的結果寫入到結果序列中,這裏將myfunc(x)結果返回一個新的List,結果是:List[Int] = List(2, 4, 6, 8, 10)
下面咱們引入面向函數編程最經常使用的一個函數map:
myList.map((x: Int) => x * 2)
結果也是List[Int] = List(2, 4, 6, 8, 10); 也至關以將集合myList中每一個元素通過(x: Int) => x * 2運算獲得結果。
從輸入輸出這個角度理解這個函數map,map的輸入是(x: Int) => x * 2的輸出,而(x: Int) => x * 2的輸入是什麼呢?是x,那麼x從哪裏來的?猜想多是來自myList的每一個元素。
這裏引入一個函數組合子(Functional Combinators)定義,組合子是一個沒有自由free變量的函數。什麼叫自由變量?沒有孤立的變量,好比上面的變量是x不該該是一個孤立變量,其來自於myList的元素。
這裏的map對列表myList中的每個元素都應用了 x * 2函數,並返回一個新的列表List(2, 4, 6)。咱們稱這個操做是map 組合子,同理還有filter操做。
for(x <- c; if cond) yield {...}
等同於:
c.filter(x => cond).map(x => {...})
或
c.withFilter(x => cond).map(x => {...})
注意到這裏for中多了一個if語句判斷,也就是對列表集合中元素進行if判斷,這至關於使用了filter函數,filter對傳入函數計算結果爲false的所有刪除。返回一個布爾值的函數一般被稱爲謂詞函數[或斷定函數]。
再看看for的另一種形式, 集合嵌套:
for(x <- c1; y <- c2; z <-c3) {...}
等同於:
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
使用組合子foreach的好處這裏也能夠看出,使用for進行嵌套循環,常常可能會迷糊鑽暈了,而使用組合子則簡單明瞭。foreach 這個組合子相似Map,可是不返回任何結果,
val doubled = myList.foreach((x: Int) => x * 2) doubled: Unit = ()
這裏foreach返回結果爲類型Unit,相似void,是空。
flatMap 是另一個組合子,flat能夠理解爲摺疊或嵌套的意思。
for(x <- c1; y <- c2; z <- c3) yield {...}
等同於
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
這裏的for循環和上面區別多了一個yield,看到yield第一反應咱們是想到map,可是這裏集合不是一個,而是三個嵌套,那麼咱們就使用flat+map.注意到,z是嵌套集合最後一個輸出,做爲map的輸入。
再看一個例子,假設有嵌套集合以下:
val nestedNumbers = List(List(1, 2), List(3, 4))
nestedNumbers.flatMap(x => x.map(_ * 2)
返回結果是
List[Int] = List(2, 4, 6, 8)
將兩個集合摺合成一個集合輸出,並應用了x*2函數計算。這裏_ * 2等同於(x: Int) => x * 2,下劃線_表示通配上下文的任何變量或函數。是一種簡單寫法。
讓咱們仍是圍繞(x: Int) => x * 2繼續展開,它表明一個有輸入和輸出的函數,若是咱們在另一個函數中須要用這個函數做爲輸入參數,那麼如何定義另一個函數的方法參數呢?myfunc2(_*2)是一種hard code寫法。
def myfunc2(fn: Int => Int): Int = {
fn(10)
}
這裏的fn: Int => Int匹配(x: Int) => x * 2這樣的抽象,固然也能夠是(x: Int) => x + 2等,只要輸入和輸出返回都是整數便可。若是咱們運行:
myfunc2((x: Int) => x * 2)
結果是20, 而運行:
myfunc2((x: Int) => x + 2)
結果是12。
在這裏,fn(10)中的10是fn輸入參數,fn輸出結果是根據myfunc2的輸入決定的,有點相似訪問者模式哦。
下面咱們嘗試寫本身的函數組合子:
var myfunc = (x: Int) => x * 2
val numbers = List(1, 2, 3, 4)
def ourMap(numbers: List[Int], fn: Int => Int): List[Int] = {
numbers.map((x: Int) => x * 2)
}
ourMap(numbers, myfunc(_))
結果是List[Int] = List(2, 4, 6, 8)