scala中的map函數與for中的yield

首先咱們從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)

相關文章
相關標籤/搜索