13.1 偏函數(partial function) 13.1.1 需求 -> 思考 一個集合val list = List(1,2,3,4,"abc"),完成如下要求 1) 將集合list中的所有數字+1,並返回一個新的集合 2) 要求忽略掉非數字的元素,即返回的新的集合形式為(2,3,4,5) ...
13.1 偏函數(partial function)
13.1.1 需求 -> 思考
一個集合val list = List(1,2,3,4,"abc"),完成如下要求
1) 將集合list中的所有數字+1,並返回一個新的集合
2) 要求忽略掉非數字的元素,即返回的新的集合形式為(2,3,4,5)
13.1.2 解決方式-filter+map返回新的集合,引出偏函數
13.1.3 解決方式-模式匹配
object boke_demo01 { def main(args: Array[String]): Unit = { //思路1 filter + map 方式解決 //雖然可以解決問題,但是麻煩. val list = List(1, 2, 3, 4, "hello") // 先過濾,再map println(list.filter(f1).map(f3).map(f2)) //思路2-模式匹配 //小結:雖然使用模式匹配比較簡單,但是不夠完美 val list2 = list.map(addOne2) println("list2=" + list2) } //模式匹配 def addOne2(i: Any): Any = { i match { case x: Int => x + 1 case _ => } } def f1(n: Any): Boolean = { n.isInstanceOf[Int] } def f2(n: Int): Int = { n + 1 } //將Any->Int [map] def f3(n: Any): Int = { n.asInstanceOf[Int] } }
13.1.4 偏函數快速入門
-使用偏函數解決前面的問題
-案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { //使用偏函數解決 val list = List(1, 2, 3, 4, "hello") //定義一個偏函數 //1. PartialFunction[Any,Int] 表示偏函數接收的參數類型是Any,返回類型是Int //2. isDefinedAt(x: Any) 如果返回true ,就會去調用 apply 構建對象實例,如果是false,過濾 //3. apply 構造器 ,對傳入的值 + 1,並返回(新的集合) val partialFun = new PartialFunction[Any, Int] { override def isDefinedAt(x: Any) = { println("x=" + x) x.isInstanceOf[Int] } override def apply(v1: Any) = { println("v1=" + v1) v1.asInstanceOf[Int] + 1 } } //使用偏函數 //說明:如果是使用偏函數,則不能使用map,應該使用collect //說明一下偏函數的執行流程 //1. 遍歷list所有元素 //2. 然後調用 val element = if(partialFun-isDefinedAt(list單個元素)) {partialFun-apply(list單個元素) } //3. 每得到一個 element,放入到新的集合,最後返回 val list2 = list.collect(partialFun) println("list2" + list2) } }
13.1.5 偏函數的小結
1) 使用構建特質的實現類(使用的方式是PartialFunction的匿名子類)
2) PartialFunction是個特質
3) 構建偏函數時,參數形式[Any,Int]是泛型,第一個表示參數類型,第二個表示返回參數
4) 當使用偏函數時,會遍歷集合的所有元素,編譯器執行流程時先執行isDefinedAt(),如果為true,就會執行apply,構建一個新的對象返回
5) 執行isDefinedAt()為false就過濾掉這個元素,即不構建新的Int對象
6) map函數不支持偏函數,因為map底層的機制就是所有迴圈遍歷,無法過濾處理原來集合的元素
7) collect函數支持偏函數
13.1.6 偏函數的簡寫形式
object boke_demo01 { def main(args: Array[String]): Unit = { //可以將前面的案例的偏函數簡寫 def partialFun: PartialFunction[Any, Int] = { //簡寫成case 語句 case i: Int => i + 1 case j: Double => (j * 2).toInt } val list = List(1, 2, 3, 4, 1.2, 2.4, 1.9f, "hello") val list2 = list.collect(partialFun) println("list2=" + list2) //第二種簡寫形式 val list3 = list.collect { case i: Int => i + 1 case j: Double => (j * 2).toInt case k: Float => (k * 3).toInt } println("list3=" + list3) // (2,3,4,5) } }
13.2 作為參數的函數
13.2.1 基本介紹
函數作為一個變數傳入到另一個函數中,那麼該作為參數的函數的類型是:function1,即:(參數類型) => 返回類型
13.2.2 應用案例
object boke_demo01 { def main(args: Array[String]): Unit = { def plus(x: Int) = 3 + x //說明 val result = Array(1, 2, 3, 4).map(plus(_)) println(result.mkString(",")) //4,5,6,7 //說明 //1. 在scala中,函數也是有類型,比如plus就是 <function1> println("puls的函數類型function1" + (plus _)) } }
13.2.3 對案例演示的小結
1) map(plus(_))中的plus(_)就是將plus這個函數當作一個參數傳給了map,_這裡代表從集合中遍歷出來的一個元素
2) plus(_)這裡也可以寫成plus表示對Array(1,2,3,4)遍歷,將每次遍歷的元素傳給plus的x
3) 進行 3+x 運算後,返回新的Int,並加入到新的集合result中
4) def map[B,That](f:A=>B)的聲明中的 f:A=>B 的一個函數
13.3 匿名函數
13.3.1 基本介紹
沒有名字的函數就是匿名函數,可以通過函數表達式來設置匿名函數
13.3.2 應用案例
object boke_demo01 { def main(args: Array[String]): Unit = { //對匿名函數的說明 //1. 不需要寫 def 函數名 //2. 不需要寫返回類型,使用類型推導 //3. = 變成 => //4. 如果有多行,則使用{} 包括 val triple = (x: Double) => { println("x=" + x) 3 * x } println("triple=" + triple(3)) // 9.0 } }
13.4 高階函數
13.4.1 基本介紹
能夠接受函數作為參數的函數,叫做高階函數(higher-order function)。可使應用程式更加健壯
13.4.2 高階函數基本使用
object boke_demo01 { def main(args: Array[String]): Unit = { def test(f: Double => Double, f2: Double => Int, n1: Double) = { f(f2(n1)) // f(0) } //sum 是接收一個Double,返回一個Double def sum(d: Double): Double = { d + d } def mod(d: Double): Int = { d.toInt % 2 } val res = test(sum, mod, 5.0) // println("res=" + res) // 2.0 } }
13.4.3 高階函數可以返回函數類型
object boke_demo01 { def main(args: Array[String]): Unit = { //說明 //1. minusxy是高階函數,因為它返回匿名函數 //2. 返回的匿名函數 (y: Int) => x - y //3. 返回的匿名函數可以使用變數接收 def minusxy(x: Int) = { (y: Int) => x - y //匿名函數 } //分步執行 //f1 就是 (y: Int) => 3 - y val f1 = minusxy(3) println("f1的類型=" + f1) println(f1(1)) // 2 println(f1(9)) // -6 //也可以一步到位的調用 println(minusxy(4)(9)) // -5 } }
13.5 參數(類型)推斷
13.5.1 基本介紹
參數推斷省去類型信息(在某些情況下[需要有應用場景],參數類型是可以推斷出來的,如list=(1,2,3) list.map() map中函數參數類型是可以推斷的),同時也可以進行相應的簡寫
13.5.2 參數類型推斷寫法說明
1) 參數類型是可以推斷時,可以省略參數類型
2) 當傳入的函數,只有單個參數時,可以省去括弧
3) 如果變數只在=>右邊只出現一次,可以用_來代替
13.5.3 應用案例
object boke_demo01 { def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4) println(list.map((x: Int) => x + 1)) //(2,3,4,5) println(list.map((x) => x + 1)) //(2,3,4,5) println(list.map(x => x + 1)) //(2,3,4,5) println(list.map(_ + 1)) //(2,3,4,5) println(list.reduce(f1)) // 10 println(list.reduce((n1: Int, n2: Int) => n1 + n2)) //10 println(list.reduce((n1, n2) => n1 + n2)) //10 println(list.reduce(_ + _)) //10 val res = list.reduce(_ + _) } def f1(n1: Int, n2: Int): Int = { n1 + n2 } }
13.6 閉包
13.6.1 基本介紹
閉包就是一個函數和與其相關的引用環境組合的一個整體(實體)
13.6.2 案例演示1
object boke_demo01 { def main(args: Array[String]): Unit = { //1.用等價理解方式改寫 2.對象屬性理解 def minusxy(x: Int) = (y: Int) => x - y //f 函數就是閉包. val f = minusxy(20) println("f(1)=" + f(1)) // 19 println("f(2)=" + f(2)) // 18 } }
-對上述案例演示的小結和說明
1) (y: Int) => x - y 返回的是一個匿名函數,因為該函數引用到函數外的x,那麼該函數和x整體形成一個閉包。如:這裡val f = minusxy(20)的f函數就是閉包
2) 可以這樣理解,返回函數是一個對象,而x就是該對象的一個欄位,它們共同形成一個閉包
3) 當多次調用f時(可以理解多次調用閉包),發現使用的是同一個x,所以x不變
4) 在使用閉包時,主要搞清楚返回函數引用了函數外的哪些變數,因為它們會組合成一個整體(實體),形成一個閉包
13.6.3 案例演示2
object boke_demo01 { def main(args: Array[String]): Unit = { /* 請編寫一個程式,具體要求如下 1.編寫一個函數 makeSuffix(suffix: String) 可以接收一個文件尾碼名(比如.jpg),並返回一個閉包 2.調用閉包,可以傳入一個文件名,如果該文件名沒有指定的尾碼(比如.jpg) ,則返回 文件名.jpg , 如果已經有.jpg尾碼,則返回原文件名。 比如 文件名 是 dog =>dog.jpg 比如 文件名 是 cat.jpg => cat.jpg 3.要求使用閉包的方式完成 提示:String.endsWith(xx) */ //使用並測試 val f = makeSuffix(".jpg") println(f("dog.jpg")) // dog.jpg println(f("cat")) // cat.jpg } def makeSuffix(suffix: String) = { //返回一個匿名函數,回使用到suffix (filename:String) => { if (filename.endsWith(suffix)) { filename } else { filename + suffix } } } }
13.7 函數柯里化(curry)
13.7.1 基本介紹
1) 函數編程中,接受多個參數的函數都可以轉化為接受單個參數的函數,這個轉化過程就叫柯里化
2) 柯里化就是證明瞭函數只需要一個參數而已
13.7.2 函數柯里化快速入門
//編寫一個函數,接收兩個整數,可以返回兩個數的乘積,要求: //使用常規的方式完成 //使用閉包的方式完成 //使用函數柯里化完成 def mul(x: Int, y: Int) = x * y println(mul(10, 10)) def mulCurry(x: Int) = (y: Int) => x * y println(mulCurry(10)(9)) def mulCurry2(x: Int)(y:Int) = x * y println(mulCurry2(10)(8))
13.7.3 函數柯里化應用案例
object boke_demo01 { def main(args: Array[String]): Unit = { //這是一個函數,可以接收兩個字元串,比較是否相等 def eq(s1: String, s2: String): Boolean = { s1.equals(s2) } //隱式類 implicit class TestEq(s: String) { //體現了將比較字元串的事情,分解成兩個任務完成 //1. checkEq 完轉換大小寫 //2. f函數完成比較任務 def checkEq(ss: String)(f: (String, String) => Boolean): Boolean = { f(s.toLowerCase, ss.toLowerCase) } } val str1 = "hello" println(str1.checkEq("HeLLO")(eq)) //在看一個簡寫形式 println(str1.checkEq("HeLLO")(_.equals(_))) } }
13.8 控制抽象
13.8.1 看一個需求
//如何實現將一段代碼(從形式上看),作為參數傳遞給高階函數,在高階函數內部執行這段代碼 //其使用的形式如 breakable{} var n = 10 breakable { while (n <= 20) { n += 1 if (n == 18) { break() } } }
13.8.2 控制抽象基本介紹
-控制抽象是這樣的函數,滿足如下條件
1) 參數是函數
2) 函數參數沒有輸入值也沒有返回值
-控制抽象應用案例(使用控制抽象實現了while語法)
object boke_demo01 { def main(args: Array[String]): Unit = { //myRunInThread 就是一個抽象控制 //是沒有輸入,也沒有輸出的函數 f1: () => Unit def myRunInThread(f1: () => Unit) = { new Thread { override def run(): Unit = { f1() //只寫了 f1 } }.start() } myRunInThread { () => println("幹活咯!5秒完成...") Thread.sleep(5000) println("幹完咯!") } //簡寫形式 def myRunInThread2(f1: => Unit) = { new Thread { override def run(): Unit = { f1 //只寫了 f1 } }.start() } //對於沒有輸入,也沒有返回值函數,可以簡寫成如下形式 myRunInThread2 { println("幹活咯!5秒完成...~~~") Thread.sleep(5000) println("幹完咯!~~~") } } }
13.8.3 進階用法:實現類似while的until函數
object boke_demo01 { def main(args: Array[String]): Unit = { var x = 10 //說明 //1 函數名為 until , 實現了類似 while迴圈的效果 //2. condition: => Boolean 是後一個沒有輸入值,返回Boolean類型函數 //3. block: => Unit 沒有輸入值,也沒有返回值 def mywhile(condition: => Boolean)(block: => Unit): Unit = { //類似while迴圈,遞歸 if (!condition) { block // x= 9 ,x = 8 x =7 .... mywhile(condition)(block) } } mywhile(x == 0) { x -= 1 println("x=" + x) } } }