scala基礎內容 函數式編程與基礎函數,匿名函數,遞歸練習
函數式編程
scala是完全面向函數式編程語言
函數式編程最關心的是問題的解決方案(封裝的功能),重點在於函數的功能,函數的入參和函數的出參
函數式編程完全就是依託於函數而實現的
scala中函數的關鍵字:def
普通的函數
有參,有返回值
//i : Int i 入參的參數名 Int 入參的類型,整形
//:Int= 有返回值,返回值類型爲Int
//return i; 返回語句,返回值爲i
def test(i : Int):Int={
return i;
}
print(test(2))
無參,無返回值
//:Unit= 無返回值,返回值類型爲Unit
def test1():Unit={
print("test1")
}
test1()
//如果函數聲明時,明確無返回值(Unit),那麼即使函數中有return也不起作用
//輸出內容爲 ()
def test3(): Unit ={
return "aaa"
}
print(test3());
有參,無返回值
//輸出內容爲 sss+++test2
def test2(i : String): Unit ={
print(i+"+++test2")
}
test2("sss")
def test3(): Unit ={
return "aaa"
}
print(test3());
scala至簡原則
1.如果將函數的最後一行代碼作爲返回值,那麼return關鍵字可以省略
2.如果可以根據函數的最後一行來推斷返回類型,那麼返回值類型可以省略
3.如果函數體中只有一行代碼,大括號可以省略
4.如果函數聲明中沒有參數列表,小括號可以省略,如果函數小括號省略,那麼訪問函數時不能增加小括號,def關鍵字用來區分是變量還是函數,所以def關鍵字不能省略
5.如果函數沒有參數列表,但是沒有省略小括號,調用時可加可不加
6.如果明確函數沒有返回值,那麼等號可以省略,省略後,編譯器不會將最後一行代碼作爲返回值
//如果將函數的最後一行代碼作爲返回值,那麼return關鍵字可以省略
def test4(): String ={
"aaa"
}
print(test4());
//如果可以根據函數的最後一行來推斷返回類型,那麼返回值類型可以省略
def test5()={
"aaa"
}
print(test5());
//如果函數體中只有一行代碼,大括號可以省略
def test6()="aaa"
print(test6());
//如果函數聲明中沒有參數列表,小括號可以省略
//如果函數小括號省略,那麼訪問函數時不能增加小括號
//def關鍵字用來區分是變量還是函數,所以def關鍵字不能省略
def test7 = "test";
//常量val
val test8 = "test2"
println(test7)
print(test8)
//如果函數沒有參數列表,但是沒有省略小括號,調用時可加可不加
println(test4)
//如果明確函數沒有返回值,那麼等號可以省略,省略後,編譯器不會將最後一行代碼作爲返回值
def test9(){
"test9"
}
println(test9)
匿名函數
//不需要外部引用,直接執行,類似於java的代碼塊,符號爲->
()->{
println(5555)
}
//調用執行,符號爲=>
val test10: () => Unit = () => {
println("aaa")
}
test10()
可變參數
scala中用 * 來標記可變參數,與java中的…作用一樣
//用*來表示可變參數
def test11(name : String*): Unit ={
println(name)
}
//調用函數的時候,可以傳零個到多個
//輸出結果爲 WrappedArray(aa, bb)
test11("aa","bb")
//輸出結果爲 List()
test11()
默認參數
//如果希望函數中的某個參數使用默認值,那麼可以在聲明時直接賦值
//scala中沒有重載的概念,所以可以使用默認參數來實現重載的效果
def test12(name : String,age : Int = 20): Unit ={
println(s"${name}+${age}");
}
//輸出結果爲 test12+20
test12("test12")
//輸出結果爲 test12+50
test12("test12",50)
//儘量不要把默認參數放到首位,在調用時會出錯,可以使用帶名參數訪問
//調用函數時,參數匹配規則爲從前到後,從左到右
def test12(name : String = "aa",age : Int): Unit ={
println(s"${name}+${age}");
}
//編譯報錯
// test12("test12");
//帶名參數,將傳輸的參數與聲明的參數對應
test12(age = 52)
函數的高級使用
//TODO scala是完全面向函數式編程語言
//函數在scala中可以做任何事情
//函數可以賦值給變量,函數可以作爲函數的參數,函數可以作爲函數的返回值
def fun(): Unit ={
println("fun")
}
def fun0() ={
//返回函數
//在函數體中直接返回函數,會有問題
//fun
//需要增加特殊符號:下劃線(_)
fun _
}
//使用雙小括號來進行調用
fun0()()
//進行一定優化,函數中嵌套函數
def fun1() ={
def fun2(): Unit ={
println("fun2");
}
fun2 _
}
fun1()()
函數科裏化
柯里化(Currying)指的是將原來接受兩個參數的函數變成新的接受一個參數的函數的過程。新的函數返回一個以原有第二個參數爲參數的函數。
def fun3()(): Unit ={
println("fun3++科裏化")
}
fun3()()
閉包
閉包是一個函數,返回值依賴於聲明在函數外部的一個或多個變量。
閉包通常來講可以簡單的認爲是可以訪問一個函數裏面局部變量的另外一個函數。
//一個函數在實現邏輯時,將外部的變量引入到函數的內部,改變了這個變量的生命週期,稱之爲閉包
def fun4(i : Int)={
//改變i的作用域,使得i能夠在fun4聲明完之後,依然存在於fun5中
def fun5(j : Int=20)={
i * j
}
fun5 _
}
//當fun4聲明完畢之後,i應該是已經出棧的,但是內部的函數fun5卻又需要調用變量i
//於是scala就有了閉包的概念,將變量i的生命週期發生變化,使得變量i能夠在fun4聲明完之後,依然存在於fun5中
val intToInt = fun4(4)
println(intToInt(10))
函數作爲函數的參數
//定義無參函數fun6,返回值爲Int類型的5
def fun6(): Int ={
5
}
//用 () => 類型 來聲明函數參數
//定義函數fun7,參數爲 返回類型爲Int的無參函數
def fun7(f : () => Int): Int ={
f() + 50
}
//打印結果爲55
println(fun7(fun6))
//使用 匿名函數 作爲參數
def fun8(f : () => Unit)={
f()
}
fun8(()=>{println("匿名函數---fun8")})
//使用 有參 匿名函數 作爲參數
def fun9(f:(Int) => Unit): Unit ={
f(20)
}
fun9((i:Int)=>{print(i)})
//至簡原則
//因爲函數中定義爲Int,所以可以在調用的時候省略Int
fun9((i)=>{print(i)})
//因爲只有一行,可以省略{}
fun9((i)=>print(i))
//當參數值用到一次的情況下,可以省略(i)=>,並用_來替換i
fun9(print(_))
//因爲只有一個參數,所以(_)可以省略
fun9(print)
//兩參數求和
def fun10(f: (Int, Int) => Int): Int = {
f(20, 20)
}
//定義函數來調用
def fun11(x: Int, y: Int) = {
x + y
}
println(fun10(fun11))
//用匿名函數來調用
println(fun10((x: Int, y: Int) => {
x + y
}))
//簡化
println(fun10((x, y) => x + y))
//只用一次,所以x和y都用_代替
println(fun10(_ + _))
遞歸
遞歸:
- 函數的邏輯代碼中調用自身
- 函數在調用自身時,傳遞的參數應該有規律
- 函數應該有跳出的邏輯,否則出現死循環
//遞歸練習
//函數的邏輯代碼中調用自身
//函數在調用自身時,傳遞的參數應該有規律
//函數應該有跳出的邏輯,否則出現死循環
//遞歸函數無法推斷出函數的返回值類型,所以必須要聲明
//階乘
//定義一個名爲!!的函數,參數爲Int型,返回值爲Int
def !!(i: Int): Int = {
if (i > 1) {
i * !!(i - 1)
} else {
1
}
}
println(!!(5))