Scala的函數

Scala的函數

一、函數的聲明

    scala函數經過def關鍵字定義,def前面能夠具備修飾符,能夠經過private、protected來控制其訪問權限。es6

    注意:沒有public,不寫默認就是public的。此外也可跟上override,final等關鍵字修飾。數組

1.格式

    [private/protected] def 函數名(參數列表):返回值聲明 = {函數體}閉包

2.函數的返回值

    1)函數體中return關鍵字每每能夠省略掉,一旦省略掉,函數將會返回整個函數體中最後一行表達式的值,這也要求整個函數體的最後一行必須是正確類型的值的表達式。ide

    2)大部分時候scala均可以經過=符號來自動推斷出返回值的類型,因此一般返回值類型聲明能夠省略。函數

    可是注意:若是由於省略了返回值類型形成歧義,則必定要寫上返回值聲明。oop

    3)若是函數體只有一行內容,則包裹函數體的大括號能夠省略。優化

    4)若是返回值類型是UNIT,則另外一種寫法是能夠去掉返回值類型和等號,把方法體寫在花括號內,而這時方法內不管返回什麼,返回值都是UNIT。至關於Java中的void。編碼

    示例:es5

//方法的返回值爲空
    private def f1(){}
	protected def f2():String={"hello"}
	def f3()={"hello"}
	//若是函數體只一行內容,能夠省了花括號
	def f4()="hello"
	//定義方法參數類型,返回值類型,及返回值
	def f5(a:Int,b:Int)={a+b}

3.默認參數

    能夠爲函數的參數設置默認值。spa

    示例:

//默認參數的使用
	def f8(a:String,b:String="[",c:String="]")={
		b+a+c
	}

4.佔位符

    佔位符:佔位符指的是scala中的下劃線_ ,能夠用它看成一個或多個參數來使用。

    使用_佔位符的前提要求:每一個參數在函數僅出現一次。

    使用下劃線時,若是類型能夠自動推斷出,則不用聲明類型。若是沒法自動推斷類型,則在下劃線後本身來顯示聲明類型便可。

示例:

//要求經過reduceLeft函數計算階乘結果
val a2=Array(1,2,3,4)
a2.reduceLeft{(a:Int,b:Int)=>{a*b}}
a2.reduceLeft{_*_}

二、函數的種類

    Scala中的函數分爲成員函數、本地函數(內嵌在函數內的函數)、函數值(匿名函數)、高階函數。

1.成員函數

    成員函數:函數被使用在類的內部,做爲類的一份子,稱爲類的成員函數。

    示例:

class Person {
  //eat方法是Person的成員方法
  def eat() {
    println("eat")
  }
}

2.本地函數

    本地函數:函數內嵌的函數稱爲本地函數,這樣的函數外界沒法訪問。

    示例:

class Person {
  //eat方法是Person的成員方法
  def eat() {
    println("eat")
    //本地函數:內嵌在函數內的函數。對象不能直接調用本地函數。
    def cook() {
      println("cook")
    }
  }
}

3.匿名函數

    函數值(匿名函數):

    1.匿名函數沒有函數名。

    2.匿名函數的做用是配合高階函數來使用的,匿名函數能夠做爲函數的參數進行傳遞。

    示例:

(a:Int,b:Int)=>{a+b}
(a:Int,b:Int)=>a+b
val a1=Array(1,2,3,4)
//a=1	b=2	a+b=3
//a=3	b=3	a+b=6
//a=6	b=4 a+b=10
a1.reduceLeft{(a:Int,b:Int)=>a+b}
a1.reduceLeft{(a,b)=>a+b}
a1.foreach{(x:Int)=>{println(x)}}
a1.foreach{x=>println(x)}

4.高階函數

    高階函數:函數能夠做爲方法的參數進行傳遞和調用。

    示例:

//定義一個高階函數,能夠將函數看成參數傳遞
  def f2(a:Int,b:Int,f:(Int,Int)=>Int)={
  	f(a,b)
  }
  f2(2,3,(a:Int,b:Int)=>{a+b})
  f2(2,3,(a,b)=>a+b)
  f2(2,3,(a,b)=>a*b)
  f2(2,3,_*_)
  //定義一個高階函數,要求:傳入一個String類型的參數,以及一個處理String類型的匿名函數
  //此高階函數的返回值就是匿名函數的返回值
  def f3(a:String,f:(String)=>Array[String])={
  	f(a)
  }
  //注意:匿名函數必定要和指定返回值類型匹配
  f3("hello,world",(a:String)=>{a.split(",")})
  f3("hello,world",a=>a.split(","))
  f3("hello,world",a=>a split ",")
  f3("hello,world",_.split(","))
  //要求經過reduceLeft函數計算階乘結果
  val a2=Array(1,2,3,4)
  a2.reduceLeft{(a:Int,b:Int)=>{a*b}}

三、遞歸

    想要實現遞歸方法,有兩個要素能夠快速的實現。

    要素1:找出遞歸結束的條件。

    要素2:找出函數的映射關係。

    scala中,若是在遞歸時,保證函數體的最後一行爲遞歸調用,則稱這樣的遞歸爲尾遞歸。scala會針對尾遞歸作優化處理,因此建議在寫遞歸時寫成尾遞歸形式。

    範例:

    斐波那契數列:1 1 2 3 5 8 13 ?

    要素1:找出遞歸結束的條件:f(n)=f(n-1)+f(n-2)

    要素2:找出函數的映射關係:f(0)=1;f(1)=1

    因此代碼就能夠爲:

def f1(n:Int):Int={
  	if(n==0)return 1
  	if(n==1)return 1
  	else f1(n-1)+f1(n-2)
}

    示例:

//從1開始作加法,只加偶數,當加和累計超過50時,結束遞歸
  //示意:2+4+6……
  def f1(num: Int, sum: Int): Int = {
    if (sum > 50) return sum;
    if (num % 2 == 0) { f1(num + 1, sum + num) }
    else { f1(num + 1, sum) }
  }

  //2 3 4 9 8 27 16 ?
  //f(n)= f(n-2)*2
  //f(n)=f(n-2)*3
  //f(0)=2;f(1)=3
  def f2(n: Int): Int = {
    if (n == 0) return 2
    if (n == 1) return 3
    if (n % 2 == 0) return f2(n - 2) * 2
    else f2(n - 2) * 3
  }

  //2 3 4 9 16 81 ?
  // n的取值:f(0) f(1) f(2)  f(3)  f(4) f(5)
  //當n爲偶數時,f(n)=f(n-2)*f(n-2)
  //當n爲奇數是,f(n)=f(n-2)*f(n-2)
  //fn=f(n-2)*f(n-2)
  def f3(n: Int): Int = {
    if (n == 0) return 2
    if (n == 1) return 3
    else f3(n - 2) * f3(n - 2)
  }

  //求 1~n的數字之和
  //1    3      6     10     15
  //f(0) f(1)  f(2)   f(3)   f(4)
  //f(n)=f(n-1)+n+1
  def f5(n: Int): Int = {
    if (n == 0) return 1
    else f5(n - 1) + n + 1
  }

  //給定一個初始值n,並設定sum的初始值爲0,當求和sum>12時結束遞歸
  //0  1  3   6   10  15
  //f(0,0)——n=0,sum=0
  //f(1,1)——n=1,sum=1
  //f(2,3)——n=2,sum=3
  //f(3,6)——n=3,sum=6
  //f(n+1,sum+n)
  def f6(n: Int, sum: Int): Int = {
    if (sum > 12) return sum
    else f6(n + 1, sum + n)
  }

  //給定一個scope範圍,計算0~scope範圍的整數之和,
  //當和>12或者達到scope邊界時,結束遞歸
  def f7(n: Int, sum: Int, scope: Int): Int = {
    if (sum > 12) return sum
    if (n - 1 == scope) return sum
    else f7(n + 1, sum + n, scope)
  }

四、變長參數

    在scala中,能夠指明函數的最後一個參數是重複的。從而容許客戶向函數傳入可變參數的列表。

    想要標註一個重複參數,能夠在參數的類型以後放一個星號。重複參數(可變參數)的類型是聲明參數類型的數組。

    示例:

//定義變長參數
  def f1(num:Int*)={}
  def f2(num:Int*)={
  	for(i<-num)println(i)
  }
  f2(1,2,3,4,5)
  def f21(a:Int,str:String*)={}

五、柯里化 Currying

1.介紹

    scala的柯里化的做用是結合scala的高階函數,從而容許用戶自創建控制結構。

    柯里化(Currying)技術 Christopher Strachey 以邏輯學家 Haskell Curry 命名的(儘管它是 Moses Schnfinkel 和 Gottlob Frege 發明的)。它是把接受多個參數的函數變換成接受一個單一參數的函數,而且返回接受餘下的參數且返回結果的新函數的技術。

案例

//首先咱們定義一個函數:
    def f1(a:Int,b:Int):Int={a+b}
    //如今咱們把這個函數變一下形:
    def f2(a:Int)(b:Int)={a+b}
    //那麼咱們應用的時候,應該是這樣用:f2(2)(3),最後結果都同樣是5,這種方式(過程)就叫柯里化。
    val r1=f2(2)(3)
    //柯里化實質上會演變成這樣一個函數:
    //接收一個參數a,返回一個匿名函數,
    //該匿名函數又接收一個參數b,函數體爲a+b
    def f3(a:Int)=(b:Int)=>a+b
    val f4=f3(2)
    //請思考 f4(3)的值是多少?答案是:5
    f4(3)

示例

//柯里化
  def f3(a:Int,b:Int)={a+b}
  def f31(a:Int)(b:Int)={a+b}
  f3(2,3)
  f31(2)(3)

  def f4(a:Int,b:Int,c:Int)={a+b+c}
  def f41(a:Int)(b:Int)(c:Int)={a+b+c}
  def f42(a:Int,b:Int)(c:Int)={a+b+c}
  def f43(a:Int)(b:Int,c:Int)={a+b+c}

  def f5(a:Int,b:Int,f:(Int,Int)=>Int)={f(a,b)}
  def f51(a:Int)(b:Int,f:(Int,Int)=>Int)={f(a,b)}
  def f52(a:Int,b:Int)(f:(Int,Int)=>Int)={f(a,b)}
  def f53(a:Int)(b:Int)(f:(Int,Int)=>Int)={f(a,b)}
  f5(2,3,_+_)
  f52(2,3)(_+_)

4.做用

    柯里化技術在提升適用性、延遲執行或者固定易變因素等方面有着重要重要的做用,加上scala語言自己就是推崇簡潔編碼,使得一樣功能的函數在定義與轉換的時候會更加靈活多樣。另外在Spark的源碼中有大量運用scala柯里化技術的狀況,須要掌握好該技術才能看得懂相關的源代碼。

    在scala柯里化中,閉包也發揮着重要的做用。所謂的閉包就是變量出了函數的定義域外在其餘代碼塊還能其做用,這樣的狀況稱之爲閉包。就上述討論的案例而言,若是沒有閉包做用,那麼轉換後函數其實返回的匿名函數是沒法在與第一個參數a相關結合的,天然也就沒法保證其所實現的功能是跟原來一致的。

六、內置高階函數

    適用於全部集合。

1.partition

    拆分,將一個集合按一個布爾值分紅兩個集合,知足條件的一個集合,其餘另一個集合。

    按照指定原則拆分,返回的是一個二元Tuple。

val l1=List(1,2,3,4,5,6)
  l1.partition{x=> x%2==0}
//> res0: (List[Int], List[Int]) = (List(2, 4, 6),List(1, 3, 5))

2.map

    映射,把一個集合轉換爲另一個集合。集合中元素個數不變。

    改變集合類型中,元素的形式或數據,返回一個新的集合。此方法不會改變集合中元素的個數,只是改變了數值和形式。

val l2=List("hadoop","world","hello","hello")
  l2.map(x=>(x,1))
//> res1: List[(String, Int)] = List((hadoop,1), (world,1), (hello,1), (hello,1))
  val l3=List("hello word","hello hadoop")
  l3.map{x=>x.split(" ")}
//> res2: List[Array[String]] = List(Array(hello, word), Array(hello, hadoop))

3.flatMap

    扁平化map,會取出集合的元素。注意,此方法會改變集合中的元素個數。

    通常引用場景:讀取文件後,處理文件,將每行數據按指定分割符切分。

l3.flatMap{x=>x.split(" ")}
//> res3: List[String] = List(hello, word, hello, hadoop)
  //要求:操做l3將其變成(word,1)的形式
l3.flatMap{x=>x.split(" ")}.map(x=>(x,1))
//> res4: List[(String, Int)] = List((hello,1), (word,1), (hello,1), (hadoop,1))

4.filter

    過濾。

val l4=List(1,2,3,4,5)
  l4.filter(x=>x>3)
//> res5: List[Int] = List(4, 5)

5.reduce

    歸約,reduce的過程:將上次的運行結果和下一個值進行運算。

    函數接收兩個參數 => 返回一個值。

    等價於reduceLeft。

l4.reduce{_+_}
//> res6: Int = 15

6.groupBy

    按指定規則作聚合,最後將結果返回到一個map映射裏。

    按照指定原則作分組,返回的是Map類型。Map的key是分組鍵,value是對應的List集合。

val l5=List(("bj",1),("sh",2),("bj",3),("sh",4),("sz",5))
l5.groupBy{x=>x._1}
//> res7: scala.collection.immutable.Map[String,List[(String, Int)]] = Map(bj ->List((bj,1), (bj,3)), sz -> List((sz,5)), sh -> List((sh,2), (sh,4)))
l5.groupBy{case(addr,count)=>addr}
//> res8: scala.collection.immutable.Map[String,List[(String, Int)]] = Map(bj ->List((bj,1), (bj,3)), sz -> List((sz,5)), sh -> List((sh,2), (sh,4)))

7.mapValues

    此方法是針對的Map類型的值作操做,此方法只適用於Map類型。

val m1=Map("rose"->23,"tom"->25,"jary"->30)
  m1.mapValues {x=>x+10}
//> res9: scala.collection.immutable.Map[String,Int] = Map(rose -> 33, tom -> 35, jary -> 40)

8.sortBy

    排序。

 

val l6=List((2,"aaa"),(1,"bbb"),(4,"ddd"),(3,"ccc"))
  l6.sortBy{x=>x._1}
//> res10: List[(Int, String)] = List((1,bbb), (2,aaa), (3,ccc), (4,ddd))
  l6.sortBy{x=>x._2}
//> res11: List[(Int, String)] = List((2,aaa), (1,bbb), (3,ccc), (4,ddd))
  l6.sortBy{case(num,str)=>num}
//> res12: List[(Int, String)] = List((1,bbb), (2,aaa), (3,ccc), (4,ddd))

案例:統計單詞頻率

    統計出每一個單詞出現的頻次。最後的結果形式:(hello,5)(hadoop,2)……

val l7=List("hello hadoop","hello world","hello spark","hello hadoop","hello hive")
//方法一:
  l7.flatMap{line=>line.split(" ")}.groupBy{word=>word}.mapValues { list => list.size }.foreach{println(_)}
//方法二:
  l7.flatMap { line => line.split(" ") }.map { word => (word,1) }.groupBy(x=>x._1).mapValues{list=>list.size}.foreach{println(_)}
//方法三:
  l7.flatMap { line => line.split(" ") }.map { word => (word,1) }.groupBy(x=>x._1).mapValues{list=>list.map(x=>x._2).reduce(_+_)}.foreach{println(_)}
//簡化寫法:
  l7.flatMap{_.split(" ")}.map{(_,1)}.groupBy(_._1).mapValues{_.map(_._2).reduce(_+_)}.foreach{println(_)}
//要求統計單詞頻次,而後返回頻次最高的前2項結果
  l7.flatMap { line => line.split(" ") }.map { word => (word,1) }.groupBy(x=>x._1).mapValues{list=>list.map(x=>x._2).reduce(_+_)}.toList.sortBy(x=> -x._2).take(2)
//> res13: List[(String, Int)] = List((hello,5), (hadoop,2))

上一篇:Scala語法介紹

下一篇:Scala中的集合類型

相關文章
相關標籤/搜索