Scala-函數(shù)式編程
1.函數(shù)式編程
1.1 面向對象和面向過程
面向對象
- 按照功能劃分問題碍沐,將功能分解成不同的的對象,定義對象中的行為和屬性,最終通過對象的行為調用來解決問題南蹂。
- 優(yōu)點:
- 耦合度低。
- 缺點:
- 執(zhí)行效率低念恍。
面向過程
- 按照步驟解決問題六剥。
- 優(yōu)點:
- 執(zhí)行效率高。
- 缺點:
- 耦合度高峰伙。
1.2 函數(shù)式編程
函數(shù)式編程和命令式編程的區(qū)別:
- 其實面向對象和面向過程都是命令式編程(計算機的命令)疗疟,其實語言其實都是面向計算機硬件的抽象。
- int a=1瞳氓;這里a只是內存地址的引用策彤,還可以讓a=其他數(shù),是個變量匣摘。
- 命令式編程的編碼都可以翻譯成計算機底層的指令店诗,對計算機最友好的語言,是計算機的子程序音榜。
- 而函數(shù)式編程則重點關注的是<font color=red>數(shù)據(jù)的映射關系</font>(自變量因變量的映射關系)庞瘸,<font color=red> 不關心計算機底層如何實現(xiàn)</font>,這里的函數(shù)指的是數(shù)學中的函數(shù)赠叼。
- val a =1 ,函數(shù)式編程中其實沒有變量擦囊,即一個常量违霞,a=1不能再讓a=其他數(shù),這更符合數(shù)學中函數(shù)的定義瞬场。
- sacla中因為其有函數(shù)式編程的特點买鸽,所以推薦能用常量的不要用變量。
- 定義一個函數(shù)贯被,求未知數(shù)的值眼五,函數(shù)式編程中每一段程序都有一個返回值。
- 函數(shù)式編程本質就是數(shù)據(jù)的映射關系刃榨,定義一個表達式弹砚,通過該表達式不同的求值双仍,做函數(shù)的映射關系枢希。
- 函數(shù)至簡原則:能省則省 ,只專注于對應的業(yè)務代碼朱沃。
- java1.8中的函數(shù)式編程就是參照于此苞轿,簡化和業(yè)務無關的邏輯。
兩種編程范式的優(yōu)缺點:
- 命令式編程:
- 是對于計算機有好的編程逗物,因為都是對應計算機命令的編程搬卒。
- 因為最終都是翻譯成計算機命令,所以其受計算機環(huán)境的影響很大翎卓。
- 函數(shù)式編程:
- 是對于人更好理解的契邀,定義函數(shù)的理念和數(shù)學上一致,不用關心計算機底層失暴。
- 定義一個函數(shù)坯门,那么其功能就確定了,該公式是不可變的逗扒,入?yún)⒒蛘哒f未知數(shù)可以來回變古戴,但是同一個值對應的結果一定是唯一的。
- 天然適用于分布式計算:
- <font color=red>函數(shù)式編程中沒有變量矩肩,都是常量现恼,處理邏輯的過程中具有不可變性,不受外界影響</font>黍檩,天然適合在不同的機器上運行叉袍,最終只需要將各個機器上的值匯總即可,特別的適合大數(shù)據(jù)處理的分布式計算刽酱。
scala兼容了兩種編程范式的特點喳逛,scala是一個面向對象的語言,同時也是一個面向編程的語言肛跌。
2. 函數(shù)基礎
2.1 函數(shù)基本語法
2.1.1 例子
object TestFunction {
def main(args: Array[String]): Unit = {
// (1)函數(shù)定義
def f(arg: String): Unit = {
println(arg)
}
// (2)函數(shù)調用
// 函數(shù)名(參數(shù))
f("hello world")
}
}
2.2 函數(shù)和方法的區(qū)別
java中的方法強調的是:
- 類中的函數(shù)艺配,類中定義的位置是有限制的察郁。
- 比如就不能在方法中定義方法。
- 方法可以進行重載和重寫转唉。
scala中的函數(shù)
- 定義在類中的任意代碼塊中皮钠。
- Scala 語言可以在任何的語法結構中聲明任何的語法。
- 函數(shù)沒有重載和重寫的概念赠法。
- Scala 中函數(shù)可以嵌套定義麦轰。
2.2.1 例子
object FunctionAndMethod {
def main(args: Array[String]): Unit = {
// 定義函數(shù)
def sayHi(name: String): Unit = {
println("hi, " + name)
}
// 調用函數(shù),根據(jù)函數(shù)的作用域,按照就近原則砖织,先調用main中定義的sayHi函數(shù)
sayHi("alice")
// 調用對象方法款侵,為了調用方法可以如下調用
FunctionAndMethod.sayHi("bob")
// 獲取方法返回值
val result = FunctionAndMethod.sayHello("cary")
println(result)
}
// 定義對象的方法
def sayHi(name: String): Unit = {
println("Hi, " + name)
}
def sayHello(name: String): String = {
println("Hello, " + name)
return "Hello"
}
}
2.3 函數(shù)的定義
- 函數(shù) 1:無參,無返回值
- 函數(shù) 2:無參侧纯,有返回值
- 函數(shù) 3:有參新锈,無返回值
- 函數(shù) 4:有參,有返回值
- 函數(shù) 5:多參眶熬,無返回值
- 函數(shù) 6:多參妹笆,有返回值
2.3.1 例子
object FunctionDefine {
def main(args: Array[String]): Unit = {
// (1)函數(shù)1:無參,無返回值
def f1(): Unit = {
println("1. 無參娜氏,無返回值")
}
f1()
println(f1())
println("=========================")
// (2)函數(shù)2:無參拳缠,有返回值
def f2(): Int = {
println("2. 無參,有返回值")
return 12
}
println(f2())
println("=========================")
// (3)函數(shù)3:有參贸弥,無返回值
def f3(name: String): Unit = {
println("3:有參窟坐,無返回值 " + name)
}
println(f3("alice"))
println("=========================")
// (4)函數(shù)4:有參,有返回值
def f4(name: String): String = {
println("4:有參绵疲,有返回值 " + name)
return "hi, " + name
}
println(f4("alice"))
println("=========================")
// (5)函數(shù)5:多參哲鸳,無返回值
def f5(name1: String, name2: String): Unit = {
println("5:多參,無返回值")
println(s"${name1}和${name2}都是我的好朋友")
}
f5("alice","bob")
println("=========================")
// (6)函數(shù)6:多參最岗,有返回值
def f6(a: Int, b: Int): Int = {
println("6:多參帕胆,有返回值")
return a + b
}
println(f6(12, 37))
}
}
2.4 函數(shù)參數(shù)
- (1)可變參數(shù) (不確定個參數(shù)傳入)
- (2)如果參數(shù)列表中存在多個參數(shù),那么可變參數(shù)一般放置在最后
- (3)參數(shù)默認值般渡,一般將有默認值的參數(shù)放置在參數(shù)列表的后面
- (4)帶名參數(shù)(入?yún)⒖梢赃x擇性的指定某個參數(shù)進行傳值)
2.4.1 例子
object Test03_FunctionParameter {
def main(args: Array[String]): Unit = {
// (1)可變參數(shù),其實此時入?yún)⒁呀?jīng)是集合類型了懒豹,這里是數(shù)組
def f1(str: String*): Unit = {
println(str)
}
f1("alice")
f1("aaa", "bbb", "ccc")
// (2)如果參數(shù)列表中存在多個參數(shù),那么可變參數(shù)一般放置在最后
def f2(str1: String, str2: String*): Unit = {
println("str1: " + str1 + " str2: " + str2)
}
f2("alice")
f2("aaa", "bbb", "ccc")
// (3)參數(shù)默認值驯用,一般將有默認值的參數(shù)放置在參數(shù)列表的后面
def f3(name: String = "xxx"): Unit = {
println("My school is " + name)
}
f3("school")
f3()
// (4)帶名參數(shù)
def f4(name: String = "xx", age: Int): Unit = {
println(s"${age}歲的${name}在學習")
}
f4("alice", 20)
f4(age = 23, name = "bob")
f4(age = 21)
}
}
輸出
WrappedArray(alice)
WrappedArray(aaa, bbb, ccc)
str1: alice str2: WrappedArray()
str1: aaa str2: WrappedArray(bbb, ccc)
My school is school
My school is xxx
20歲的alice在學習
23歲的bob在學習
21歲的xx在學習
2.5 函數(shù)至簡原則
函數(shù)至簡原則:能省則省 脸秽,只專注于對應的業(yè)務代碼。
2.5.1 至簡原則細節(jié)
- (1)return 可以省略蝴乔,Scala 會使用函數(shù)體的最后一行代碼作為返回值
- (2)如果函數(shù)體只有一行代碼记餐,可以省略花括號
- (3)返回值類型如果能夠推斷出來,那么可以省略(:和返回值類型一起省略)
- (4)如果有 return薇正,則不能省略返回值類型片酝,必須指定
- (5)如果函數(shù)明確聲明 unit囚衔,那么即使函數(shù)體中使用 return 關鍵字也不起作用
- (6)Scala 如果期望是無返回值類型,可以省略等號
- (7)如果函數(shù)無參雕沿,但是聲明了參數(shù)列表练湿,那么調用時,小括號审轮,可加可不加
- (8)如果函數(shù)沒有參數(shù)列表肥哎,那么小括號可以省略,調用時小括號必須省略
- (9)如果不關心名稱疾渣,只關心邏輯處理篡诽,那么函數(shù)名(def)可以省略
2.5.2 例子
object Simplify {
def main(args: Array[String]): Unit = {
def f0(name: String): String = {
return name
}
println(f0("xxx"))
println("==========================")
// (1)return可以省略,Scala會使用函數(shù)體的最后一行代碼作為返回值
def f1(name: String): String = {
name
}
println(f1("xxx"))
println("==========================")
// (2)如果函數(shù)體只有一行代碼榴捡,可以省略花括號
def f2(name: String): String = name
println(f2("xxx"))
println("==========================")
// (3)返回值類型如果能夠推斷出來杈女,那么可以省略(:和返回值類型一起省略)
def f3(name: String) = name
println(f3("xxx"))
println("==========================")
// (4)如果有return,則不能省略返回值類型薄疚,必須指定
// def f4(name: String) = {
// return name
// }
//
// println(f4("xxx"))
println("==========================")
// (5)如果函數(shù)明確聲明unit碧信,那么即使函數(shù)體中使用return關鍵字也不起作用
def f5(name: String): Unit = {
return name
}
println(f5("xxx"))
println("==========================")
// (6)Scala如果期望是無返回值類型,可以省略等號
def f6(name: String) {
println(name)
}
println(f6("xxx"))
println("==========================")
// (7)如果函數(shù)無參街夭,但是聲明了參數(shù)列表,那么調用時躏筏,小括號板丽,可加可不加
def f7(): Unit = {
println("xxx")
}
f7()
f7
println("==========================")
// (8)如果函數(shù)沒有參數(shù)列表,那么小括號可以省略趁尼,調用時小括號必須省略
def f8: Unit = {
println("xxx")
}
// f8()
f8
println("==========================")
// (9)如果不關心名稱埃碱,只關心邏輯處理,那么函數(shù)名(def)可以省略
def f9(name: String): Unit = {
println(name)
}
// 匿名函數(shù)酥泞,lambda表達式
(name: String) => { println(name) }
println("==========================")
def f10 = (x:String)=>{println("wusong")}
def f11(f:String=>Unit) = {
f("")
}
f10(f0)
println(f10((x:String)=>{println("wusong")}))
// 匿名函數(shù)的簡化原則
f((name: String) => {
println(name)
})
// (1)參數(shù)的類型可以省略砚殿,會根據(jù)形參進行自動的推導
f((name) => {
println(name)
})
// (2)類型省略之后,發(fā)現(xiàn)只有一個參數(shù)芝囤,則圓括號可以省略似炎;其他情況:沒有參數(shù)和參數(shù)超過1的永遠不能省略圓括號。
f( name => {
println(name)
})
// (3)匿名函數(shù)如果只有一行悯姊,則大括號也可以省略
f( name => println(name) )
// (4)如果參數(shù)只出現(xiàn)一次羡藐,則參數(shù)省略且后面參數(shù)可以用_代替,參數(shù)名只在函數(shù)中出現(xiàn)一次
f( println(_) )
// (5) 如果可以推斷出悯许,當前傳入的println是一個函數(shù)體仆嗦,而不是調用語句,可以直接省略下劃線
f( println )
println("=========================")
}
}
3. 函數(shù)高級
3.1 高階函數(shù)
1)函數(shù)作為值傳遞
object HighOrderFunction {
def main(args: Array[String]): Unit = {
def f(n: Int): Int = {
println("f調用")
n + 1
}
// 1.普通的函數(shù)調用
val result: Int = f(123)
println(result)
// 2. 函數(shù)作為值進行傳遞先壕,有點像重命名
// f1,f2要的是函數(shù)體瘩扼,而不是觸發(fā)函數(shù)
val f1: Int=>Int = f
val f2 = f _
//f1谆甜,f2輸出的是函數(shù)對象,對象引用不同
println(f1)
println(f1(12))
println(f2)
println(f2(35))
// 3 無參函數(shù)
def fun(): Int = {
println("fun調用")
1
}
//調用函數(shù)體
val f3: ()=>Int = fun
val f4 = fun _
println(f3)
println(f4)
//注意:如果寫val f4 = fun 那這就是一次函數(shù)
}
}
輸出
f調用
124
com.pl.HighOrderFunction$$$Lambda$5/1510067370@19bb089b
f調用
13
com.pl.HighOrderFunction$$$Lambda$6/1908923184@4563e9ab
com.pl.HighOrderFunction$$$Lambda$7/1289479439@7cf10a6f
com.pl.HighOrderFunction$$$Lambda$8/6738746@7e0babb1
2)函數(shù)作為參數(shù)傳遞
object Test1 {
def main(args: Array[String]): Unit = {
// (1)定義一個函數(shù)集绰,函數(shù)參數(shù)還是一個函數(shù)簽名店印;f 表示函數(shù)名稱;(Int,Int) 表示輸入兩個 Int 參數(shù);Int 表示函數(shù)返回值
def dualEval(op:(Int,Int)=>Int,a:Int,b:Int):Int={
op(a,b)
}
// (1)定義一個函數(shù)倒慧,函數(shù)參數(shù)還是一個函數(shù)簽名按摘;f 表示函數(shù)名稱;(Int,Int) 表示輸入兩個 Int 參數(shù);Int 表示函數(shù)返回值
def add(a:Int,b:Int):Int={
a+b;
}
// (3)將 add 函數(shù)作為參數(shù)傳遞給 f1 函數(shù)纫谅,如果能夠推斷出來不是調用
println(dualEval(add,1,2))
println(dualEval((a,b)=>a+b,1,2))
}
}
3)函數(shù)作為函數(shù)返回值返回
package chapter05.test
object Test1 {
def main(args: Array[String]): Unit = {
// 3. 函數(shù)作為函數(shù)的返回值返回
// Int=>Unit 返回函數(shù)的入?yún)⒑头祷刂? def f5(): Int=>Unit = {
def f6(a: Int): Unit = {
println("f6調用 " + a)
}
f6 // 將函數(shù)直接返回
}
val f6 = f5()
println(f6)
println(f6(25))
println(f5()(25))
}
}
輸出
chapter05.test.Test1$$$Lambda$1/1989780873@47f37ef1 // 返回函數(shù)對象
f6調用 25
()
f6調用 25
()
4) 引用案例
? 其實高階函數(shù)的一個應用比較多的場合是:定義集合中數(shù)據(jù)的操作炫贤,將操作抽象出來。
object Test07_Practice_CollectionOperation {
def main(args: Array[String]): Unit = {
val arr: Array[Int] = Array(12, 45, 75, 98)
// 對數(shù)組進行處理付秕,將操作抽象出來兰珍,處理完畢之后的結果返回一個新的數(shù)組
def arrayOperation(array: Array[Int], op: Int=>Int): Array[Int] = {
for (elem <- array) yield op(elem)
}
// 定義一個加一操作
def addOne(elem: Int): Int = {
elem + 1
}
// 調用函數(shù),傳遞函數(shù) 這里可以看出arrayOperation只是定義函數(shù)處理的大致流程,具體的邏輯推遲到調用方询吴,和map的邏輯很符合
val newArray: Array[Int] = arrayOperation(arr, addOne)
println(newArray.mkString(","))
// 傳入匿名函數(shù)掠河,實現(xiàn)元素翻倍
val newArray2 = arrayOperation(arr, _ * 2)
println(newArray2.mkString(", "))
}
}
5)擴展練習
(1) 定義一個匿名函數(shù),并將它作為值賦給變量 fun猛计。函數(shù)有三個參數(shù)唠摹,類型分別為 Int,String奉瘤,Char勾拉,返回值類型為 Boolean。 要求調用函數(shù) fun(0, “”, ‘0’)得到返回值為 false盗温,其它情況均返回 true藕赞。
object Test08_Practice {
def main(args: Array[String]): Unit = {
// 1. 練習1
val fun = (i: Int, s: String, c: Char) => {
if (i == 0 && s == "" && c == '0') false else true
}
println(fun(0, "", '0'))
println(fun(0, "", '1'))
println(fun(23, "", '0'))
println(fun(0, "hello", '0'))
println("===========================")
}
}
(2) 定義一個函數(shù) func,它接收一個 Int 類型的參數(shù)卖局,返回一個函數(shù)(記作 f1)斧蜕。 它返回的函數(shù) f1,接收一個 String 類型的參數(shù)砚偶,同樣返回一個函數(shù)(記作 f2)批销。函數(shù) f2 接 收一個 Char 類型的參數(shù),返回一個 Boolean 的值蟹演。 要求調用函數(shù) func(0) (“”) (‘0’)得到返回值為 false风钻,其它情況均返回 true。
object Test08_Practice {
def main(args: Array[String]): Unit = {
// 2. 練習2
def func(i: Int): String=>(Char=>Boolean) = {
def f1(s: String): Char=>Boolean = {
def f2(c: Char): Boolean = {
if (i == 0 && s == "" && c == '0') false else true
}
f2
}
f1
}
println(func(0)("")('0'))
println(func(0)("")('1'))
println(func(23)("")('0'))
println(func(0)("hello")('0'))
// 匿名函數(shù)簡寫
def funcc(i: Int): String=>(Char=>Boolean) = {
//匿名函數(shù)首先不需要知道名字酒请,且返回值不用寫即所有參數(shù)類型的定義省略
// def f1(s: String): Char=>Boolean = {
(s: String) =>{
(c: Char)=> {
if (i == 0 && s == "" && c == '0') false else true
}
}
}
//當然還可以進一步縮寫
//如果在外側已經(jīng)將形參類型定義好骡技,那么內層的匿名函數(shù)形參也都是可以確定的
def func1(i: Int): String=>(Char=>Boolean) = {
s => c => if (i == 0 && s == "" && c == '0') false else true
}
println(func1(0)("")('0'))
println(func1(0)("")('1'))
println(func1(23)("")('0'))
println(func1(0)("hello")('0'))
// 上面的簡寫還可以進一步省略,將 String=>(Char=>Boolean)省略
// 函數(shù)的柯里化
def func2(i: Int)(s: String)(c: Char): Boolean = {
if (i == 0 && s == "" && c == '0') false else true
}
println(func2(0)("")('0'))
println(func2(0)("")('1'))
println(func2(23)("")('0'))
println(func2(0)("hello")('0'))
}
}
3.2 閉包&柯里化
閉包
- 如果一個函數(shù),訪問到了它的外部(局部)變量的值布朦,那么這個函數(shù)和他所處的 環(huán)境囤萤,稱為閉包。
- 即內部函數(shù)將依賴的外部變量保存在本函數(shù)中是趴,延長了外部函數(shù)局部變量的生命周期涛舍。
- scala中調用函數(shù)相當于創(chuàng)建了一個對象實例,對象實例在heap中唆途,改對象實例打包保存了該對象的環(huán)境(外部環(huán)境和局部變量)富雅,所以不會因為方法彈棧而丟失方法局部變量。
- 比如上面例子中的func2(0)("")('0')肛搬,實際上的調用順序是:func2>f1>f2没佑,f2并不會因為前兩者的彈棧而丟失其依賴的局部變量。
函數(shù)柯里化
- 把一個參數(shù)列表的多個參數(shù)變成多個參數(shù)列表温赔。
- 一般純函數(shù)式編程就是定義自變量和因變量之間的關系蛤奢,只有一個入?yún)ⅲ玫揭粋€因變量陶贼,不存在輸入多個自變量得出一個因變量的用法啤贩。
- 但是scala中因為需要兼容java和函數(shù)式編程,所以沒有只有一個入?yún)⒌南拗瓢菅恚梢栽试S多個入?yún)ⅰ?/li>
- 函數(shù)柯里化可以實現(xiàn)這么一個效果痹屹,每一層調用只有一個入?yún)ⅲ砸粋€參數(shù)列表多個參數(shù)實際可以變成多個參數(shù)列表腹纳。
object Test09_ClosureAndCurrying {
def main(args: Array[String]): Unit = {
def add(a: Int, b: Int): Int = {
a + b
}
// 1. 考慮固定一個加數(shù)的場景
def addByFour(b: Int): Int = {
4 + b
}
// 2. 擴展固定加數(shù)改變的情況
def addByFive(b: Int): Int = {
5 + b
}
// 3. 將固定加數(shù)作為另一個參數(shù)傳入痢掠,但是是作為”第一層參數(shù)“傳入
def addByFour1(): Int=>Int = {
val a = 4
def addB(b: Int): Int = {
a + b
}
addB
}
def addByA(a: Int): Int=>Int = {
def addB(b: Int): Int = {
a + b
}
addB
}
println(addByA(35)(24))
println(addByA(35))
val addByFour2 = addByA(4)
val addByFive2 = addByA(5)
println(addByFour2(13))
println(addByFive2(25))
// 4. lambda表達式簡寫
def addByA1(a: Int): Int=>Int = {
//def 函數(shù)名 返回值均省略
(b: Int) => {
a + b
}
}
//進一步簡寫 省略形參
def addByA2(a: Int): Int=>Int = {
//def 函數(shù)名 形參 返回值均省略
b => a + b
}
// 進一步簡寫 參數(shù)只出現(xiàn)一次,函數(shù)只有一行
def addByA3(a: Int): Int=>Int = a + _
val addByFour3 = addByA3(4)
val addByFive3 = addByA3(5)
println(addByFour3(13))
println(addByFive3(25))
// 5. 柯里化 該函數(shù)分為兩層嘲恍,幾個參數(shù)列表幾層 一旦用到柯里化,那么其底層必然是閉包
def addCurrying(a: Int)(b: Int): Int = {
a + b
}
println(addCurrying(35)(24))
}
}
輸出
59
chapter05.Test09_ClosureAndCurrying$$$Lambda$5/1510067370@2ff4f00f //println(addByA(35))實際輸出的是函數(shù)對象實例
17
30
17
30
59
3.3 遞歸
object Test10_Recursion {
def main(args: Array[String]): Unit = {
println(fact(5))
println(tailFact(5))
}
// 階乘
// 遞歸算法
// 1) 方法調用自身
// 2) 方法必須要有跳出的邏輯
// 3) 方法調用自身時雄驹,傳遞的參數(shù)應該有規(guī)律
// 4) scala 中的遞歸必須聲明函數(shù)返回值類型
// 遞歸實現(xiàn)計算階乘
def fact(n: Int): Int = {
if (n == 0) return 1
//最后一層代碼可以省略 return
fact(n - 1) * n
}
//上面這種遞歸方式有一個很大的確定就是:每層遞歸都需要產(chǎn)生新的棧幀佃牛,如果層數(shù)很多,會導致當前的棧中需要保存的棧幀非常多医舆,甚至會出現(xiàn)stack over flow
//遞歸是以耗費椃溃空間資源為代價的
//函數(shù)式編程語言中提供了一種優(yōu)化方式:每層棧幀覆蓋之前的棧幀,只消耗一個棧幀
// 尾遞歸實現(xiàn)
def tailFact(n: Int): Int = {
//尾遞歸 保存每次該層的結果currRes蔬将,將每層的結果值不停的往下傳爷速,這樣每次調用就不需要保存上一層的調用信息了
// @tailrec 可以確保寫出的是一個尾遞歸,如果不是會報錯
@tailrec
def loop(n: Int, currRes: Int): Int = {
if (n == 0) return currRes
loop(n - 1, currRes * n)
}
//從1開始算階乘
loop(n, 1)
}
}
3.4 控制抽象
控制抽象
- 強調的是參數(shù)的調用方式
- 傳值調用:將一個確定的值當做參數(shù)傳遞霞怀。
- 傳名調用:將代碼塊當做參數(shù)傳遞惫东。
object Test11_ControlAbstraction {
def main(args: Array[String]): Unit = {
// 1. 傳值參數(shù)
def f0(a: Int): Unit = {
println("a: " + a)
println("a: " + a)
}
f0(23)
def f1(): Int = {
println("f1調用")
12
}
f0(f1())
println("========================")
// 2. 傳名參數(shù),傳遞的不再是具體的值,而是代碼塊
// a: =>Int 該入?yún)⒖梢允谴a塊廉沮,返回值是int
def f2(a: =>Int): Unit = {
//如果a是代碼塊颓遏,那么a每出現(xiàn)一次就執(zhí)行一遍對應的代碼塊
println("a: " + a)
println("a: " + a)
}
f2(23)
f2(f1())
f2({
println("這是一個代碼塊")
29
})
}
}
控制抽象傳名參數(shù)的特性豐富了scala的功能,比如利用該特性實現(xiàn)自定義關鍵字滞时。
object Test12_MyWhile {
def main(args: Array[String]): Unit = {
var n = 10
// 1. 常規(guī)的while循環(huán)
while (n >= 1){
println(n)
n -= 1
}
// 2. 用閉包實現(xiàn)一個函數(shù)叁幢,將代碼塊作為參數(shù)傳入,遞歸調用 入?yún)?返回值 都是代碼塊
def myWhile(condition: =>Boolean): (=>Unit)=>Unit = {
// 內層函數(shù)需要遞歸調用坪稽,參數(shù)就是循環(huán)體
def doLoop(op: =>Unit): Unit = {
if (condition){
op
myWhile(condition)(op)
}
}
doLoop _
}
println("=================")
n = 10
myWhile(n >= 1){
println(n)
n -= 1
}
// 3. 用匿名函數(shù)實現(xiàn)
def myWhile2(condition: =>Boolean): (=>Unit)=>Unit = {
// 內層函數(shù)需要遞歸調用曼玩,參數(shù)就是循環(huán)體
op => {
if (condition){
op
myWhile2(condition)(op)
}
}
}
println("=================")
n = 10
myWhile2(n >= 1){
println(n)
n -= 1
}
// 3. 用柯里化實現(xiàn)
def myWhile3(condition: =>Boolean)(op: =>Unit): Unit = {
if (condition){
op
myWhile3(condition)(op)
}
}
println("=================")
n = 10
myWhile3(n >= 1){
println(n)
n -= 1
}
}
}
3.5惰性加載
惰性加載
- 當函數(shù)返回值被聲明為 lazy 時,函數(shù)的執(zhí)行將被推遲窒百,直到我們首次對此取值黍判,該函 數(shù)才會執(zhí)行。
- 即推遲函數(shù)的執(zhí)行時期贝咙,只有第一次需要調用的時候才觸發(fā)其邏輯样悟。
object Test13_Lazy {
def main(args: Array[String]): Unit = {
//惰性加載和控制抽象差不多,只是控制抽象將代碼塊給入?yún)⑼バ桑栊约虞d將代碼塊給val
lazy val result: Int = sum(13, 47)
println("1. 函數(shù)調用")
println("2. result = " + result)
println("4. result = " + result)
}
def sum(a: Int, b: Int): Int = {
println("3. sum調用")
a + b
}
}
輸出
1. 函數(shù)調用
3. sum調用 //sum函數(shù)第一次調用
2. result = 60
4. result = 60 //sum函數(shù)只調用一次將值傳給了常量result窟她,后續(xù)不會再調用該函數(shù)