Scala筆記整理(五):函數式編程
作為值傳遞的函數
測試代碼如下:
package cn.xpleaf.bigdata.p4.function /** * scala中關於函數的操作 */ object _01FunctionOps { def main(args: Array[String]): Unit = { functionOps1 } /** * 作為值傳遞的函數 * 將一個函數作為值傳遞給另外一個函數變量的時候,約定需要在函數後面加上:空格和下劃線 * 相當於數據庫中的別名,或者數據庫表對應的視圖 */ def functionOps1: Unit = { def sayHi(name:String) = println("Hello, " + name) def sayHello = sayHi _ sayHello("xpleaf") } }
輸出結果如下:
Hello, xpleaf
匿名函數
測試代碼如下:
package cn.xpleaf.bigdata.p4.function /** * scala中關於函數的操作 */ object _01FunctionOps { def main(args: Array[String]): Unit = { functionOps2 } /** * 匿名函數,說白了就是沒有函數名字 * 匿名函數就和java中的匿名內部類一樣,是只適合使用一次的函數 * 一般如果一個函數的參數是一個函數,這種情況下多用匿名函數來作為參數進行傳遞 */ def functionOps2: Unit = { val printName = (name:String) => println("你好," + name) printName("xpleaf") } }
輸出結果如下:
你好,xpleaf
其實前面在學習ArrayBuffer的時候已經有使用過匿名函數:
scala> val ab = ArrayBuffer[Int](3, 8, 2, 20, 5, 7) ab: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(3, 8, 2, 20, 5, 7) scala> ab.sortWith((v1, v2) => v1 > v2) res209: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(20, 8, 7, 5, 3, 2)
帶函數參數的函數(高階函數)
說明的作用有兩個(匿名函數作為參數和返回值為匿名函數),具體參考下面的測試代碼:
package cn.xpleaf.bigdata.p4.function
/**
* scala中關於函數的操作
*/
object _01FunctionOps {
def main(args: Array[String]): Unit = {
// functionOps1
// functionOps2
functionOps3
}
/**
* scala的高階函數,就是函數的 [參數是函數] 的函數,把這種函數稱為高階函數
*/
def functionOps3: Unit = {
// 1.匿名函數作為參數
def highOrderFunc(name:String, func:(String) => Unit): Unit = {
func(name)
}
highOrderFunc("xpleaf", (name:String) => println("Hello, " + name))
// 2.將匿名函數作為返回值傳遞給另外一個函數
def getGoodBayFunction(gMsg: String) = (gName: String) => println(gMsg + ", " + gName)
val goodbayFunction = getGoodBayFunction("good bye")
goodbayFunction("xpleaf")
}
}
輸出結果如下:
Hello, xpleaf
good bye, xpleaf
參數(類型)推斷
測試代碼如下:
package cn.xpleaf.bigdata.p4.function
import scala.collection.mutable.ArrayBuffer
/**
* scala中關於函數的操作
*/
object _01FunctionOps {
def main(args: Array[String]): Unit = {
// functionOps1
// functionOps2
// functionOps3
functionOps4
}
/**
* 對於匿名函數的省略寫法
*/
def functionOps4: Unit = {
val ab = ArrayBuffer[Int](1, 2, 3, 4, 5)
// val newAB = ab.map((x:Int) => x * 100)
// val newAB = ab.map((x) => x * 100)
// val newAB = ab.map(x => x * 100)
val newAB = ab.map(100 * _)
println(newAB) // ArrayBuffer(100, 200, 300, 400, 500)
}
}
輸出結果如下:
ArrayBuffer(100, 200, 300, 400, 500)
常見高階函數——map函數
遍歷集合中的每一個元素,返回也是一個集合,集合大小和之前集合相等。
- 快速產生0.1, 0.2, 0.3等方式的數字
scala> (1 to 9).map(0.1 * _).foreach(println(_))
0.1
0.2
0.30000000000000004
0.4
0.5
0.6000000000000001
0.7000000000000001
0.8
0.9
- 打印三角形
scala> (1 to 9).map("*" * _).foreach(println(_))
*
**
***
****
*****
******
*******
********
*********
在這裏,我們還用到了foreach,它和map很像,只不過它的函數並不返回任何值,foreach只是簡單的將函數應用到每個元素而已。
常見高階函數——filter函數
按照過濾條件,將原集合中不符合條件的數據過濾掉
- 輸出所有匹配某個特定條件的元素,得到一個序列中的所有偶數
scala> (1 to 9).filter(line => line % 2 == 0).foreach(println(_))
2
4
6
8
scala> (1 to 9).filter(_ % 2 ==0).foreach(println)
2
4
6
8
常見高階函數——reduce函數
scala> (1 to 9).reduce((v1:Int, v2:Int) => v1 + v2)
res4: Int = 45
scala> (1 to 9).reduce(_ + _)
res6: Int = 45
scala> (1 to 9).reduceLeft(_ + _)
res7: Int = 45
scala> (1 to 9).reduceRight(_ + _)
res8: Int = 45
可以寫下面一個函數來驗證reduce函數的執行過程:
scala> val process = (v1:Int, v2:Int) => println(s"v1=${v1}, v2=${v2}")
process: (Int, Int) => Unit = <function2>
scala> def pro(v1:Int, v2:Int):Int = {
| println(s"v1=${v1}, v2=${v2}")
| v1 + v2
| }
pro: (v1: Int, v2: Int)Int
- reduce的執行流程
scala> (1 to 9).reduce((v1:Int, v2:Int) => pro(v1, v2))
v1=1, v2=2
v1=3, v2=3
v1=6, v2=4
v1=10, v2=5
v1=15, v2=6
v1=21, v2=7
v1=28, v2=8
v1=36, v2=9
res0: Int = 45
- reductLeft的執行流程
scala> (1 to 9).reduceLeft((v1:Int, v2:Int) => pro(v1, v2))
v1=1, v2=2
v1=3, v2=3
v1=6, v2=4
v1=10, v2=5
v1=15, v2=6
v1=21, v2=7
v1=28, v2=8
v1=36, v2=9
res2: Int = 45
- reductRight的執行流程
scala> (1 to 9).reduceRight((v1:Int, v2:Int) => pro(v1, v2))
v1=8, v2=9
v1=7, v2=17
v1=6, v2=24
v1=5, v2=30
v1=4, v2=35
v1=3, v2=39
v1=2, v2=42
v1=1, v2=44
res3: Int = 45
這樣的話執行過程就非常清晰了,reduce和reduceLeft都是從左邊的操作數開始,而reduceRight是從右邊的操作數開始。
常見高階函數——sortWith函數
scala> (1 to 9).sortWith((v1:Int, v2:Int) => v1 > v2).foreach(println(_))
9
8
7
6
5
4
3
2
1
scala> (1 to 9).sortWith(_ > _).foreach(println)
9
8
7
6
5
4
3
2
1
閉包
函數的變量不在其作用域內被調用,就是閉包的概念,看下面一個例子:
def closePackage: Unit ={
def mulBy(factor:Double) = (x:Double) => factor * x
val triple = mulBy(3)
val half = mulBy(0.5)
println(triple(14) +" " + half(14)) //42, 7
}
1)mulBy的首次調用將參數變量factor設為3,。該變量在(x:Double)=>factor *x
函數的函數體內被引用。該函數被存入triple.然後參數變量factor從運行時的棧上被彈出。
2)mulBy再次被調用,這次factor被設為0.5.該變量在(x:Double)=>factor *x
函數的函數體內被引用,該函數被存入half.
每一個函數被稱為閉包(closure)。閉包有代碼和代碼用到的任何非局部變量定義構成。
其實上面的mulBy
函數就類似於下面這個:
def mulBy1(factor:Double, x:Double) = {
factor * x
}
原本還希望進一步寫成下面這樣子的:
def mulBy2(factor:Double, x:Double) = {
def work(x:Double) = {
factor * x
}
return work
}
但在scala中不支持這樣的寫法,下面這樣寫才可以:
def mulBy2(factor:Double) = {
(x:Double) => factor * x
}
// 加個return也不行
之前在Python中使用過閉包,類似上面的例子,Python就可以這樣寫:
def mulBy(factor):
def work(x):
return factor * x
return work
測試如下:
>>> def mulBy(factor):
... def work(x):
... return factor * x
... return work
...
>>> triple = mulBy(3)
>>> half = mulBy(0.5)
>>> triple(14)
42
>>> half(14)
7.0
當然,只是Python的語法格式就沒有scala那麽靈活了。
柯裏化(currying函數)
1、柯裏化(currying)指的是將原來接受2個參數的函數變成新的接受一個參數的函數的過程。新的函數返回一個以原有第二個參數作為參數的函數。
2、在函數調用的過程中,就變為了兩個函數連續調用的形式。在Spark源碼中,也有體現,所以對()()這種形式的Curring函數,一定要掌握。
以下函數接受一個參數,生成另一個接受單個參數的函數,要計算兩個數的乘積,調用如下:
/**
* curryingFunction: 柯裏化函數
*/
def curryingFunction ={
def totalSum(x:Int, y:Int) = println( x + y)
//totalSum(4,5)
def totalSumAtTime(x:Int) = (y:Int) => x + y
println(totalSumAtTime(4)(5))
}
Scala筆記整理(五):函數式編程