scala函數經過def關鍵字定義,def前面能夠具備修飾符,能夠經過private、protected來控制其訪問權限。es6
注意:沒有public,不寫默認就是public的。此外也可跟上override,final等關鍵字修飾。數組
[private/protected] def 函數名(參數列表):返回值聲明 = {函數體}閉包
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}
能夠爲函數的參數設置默認值。spa
示例:
//默認參數的使用 def f8(a:String,b:String="[",c:String="]")={ b+a+c }
佔位符:佔位符指的是scala中的下劃線_ ,能夠用它看成一個或多個參數來使用。
使用_佔位符的前提要求:每一個參數在函數僅出現一次。
使用下劃線時,若是類型能夠自動推斷出,則不用聲明類型。若是沒法自動推斷類型,則在下劃線後本身來顯示聲明類型便可。
示例:
//要求經過reduceLeft函數計算階乘結果 val a2=Array(1,2,3,4) a2.reduceLeft{(a:Int,b:Int)=>{a*b}} a2.reduceLeft{_*_}
Scala中的函數分爲成員函數、本地函數(內嵌在函數內的函數)、函數值(匿名函數)、高階函數。
成員函數:函數被使用在類的內部,做爲類的一份子,稱爲類的成員函數。
示例:
class Person { //eat方法是Person的成員方法 def eat() { println("eat") } }
本地函數:函數內嵌的函數稱爲本地函數,這樣的函數外界沒法訪問。
示例:
class Person { //eat方法是Person的成員方法 def eat() { println("eat") //本地函數:內嵌在函數內的函數。對象不能直接調用本地函數。 def cook() { println("cook") } } }
函數值(匿名函數):
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)}
高階函數:函數能夠做爲方法的參數進行傳遞和調用。
示例:
//定義一個高階函數,能夠將函數看成參數傳遞 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*)={}
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)(_+_)
柯里化技術在提升適用性、延遲執行或者固定易變因素等方面有着重要重要的做用,加上scala語言自己就是推崇簡潔編碼,使得一樣功能的函數在定義與轉換的時候會更加靈活多樣。另外在Spark的源碼中有大量運用scala柯里化技術的狀況,須要掌握好該技術才能看得懂相關的源代碼。
在scala柯里化中,閉包也發揮着重要的做用。所謂的閉包就是變量出了函數的定義域外在其餘代碼塊還能其做用,這樣的狀況稱之爲閉包。就上述討論的案例而言,若是沒有閉包做用,那麼轉換後函數其實返回的匿名函數是沒法在與第一個參數a相關結合的,天然也就沒法保證其所實現的功能是跟原來一致的。
適用於全部集合。
拆分,將一個集合按一個布爾值分紅兩個集合,知足條件的一個集合,其餘另一個集合。
按照指定原則拆分,返回的是一個二元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))
映射,把一個集合轉換爲另一個集合。集合中元素個數不變。
改變集合類型中,元素的形式或數據,返回一個新的集合。此方法不會改變集合中元素的個數,只是改變了數值和形式。
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))
扁平化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))
過濾。
val l4=List(1,2,3,4,5) l4.filter(x=>x>3) //> res5: List[Int] = List(4, 5)
歸約,reduce的過程:將上次的運行結果和下一個值進行運算。
函數接收兩個參數 => 返回一個值。
等價於reduceLeft。
l4.reduce{_+_} //> res6: Int = 15
按指定規則作聚合,最後將結果返回到一個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)))
此方法是針對的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)
排序。
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中的集合類型