1. 程式人生 > >Scala的函數式編程

Scala的函數式編程

類型 通過 簡單 執行 來講 常駐內存 erro pri null

Scala的函數式編程

  Scala的函數式編程的特點

   - 高階函數
   - 閉包
   - 模式匹配 可參考:http://blog.51cto.com/14048416/2337136
   - 單一賦值
   - 延遲計算
   - 類型推導
   - 尾部調用優化
   - 隱式轉化
  這篇博文重點介紹:高階函數、閉包、隱式轉化

1. 高階函數

   高階函數主要有兩種:將一個函數當做另外一個函數的參數

返回值是函數的函數

  • 高階函數的定義

    object Test01 {
    def main(args: Array[String]): Unit = {
    
    }
    //1.函數作為參數
    def sum1(f:(Int,Int)=>Int,x:Int,y:Int):Int ={
      f(x,y)
    }
    //2.函數作為返回值
    def sum2(x:Int)={
    (y:Int)=>x+y
    }
    
    //3.柯裏化寫法
    def sum3(f:(Int,Int))(x:Int,y:Int) = f(x,y)
    
    //4.使用隱式轉換的方式,傳入默認值
    def sum4(x:Int)(implicit  y:Int=10) =x+y
    }
  • 函數的調用

    object Test01 {
    def main(args: Array[String]): Unit = {
    //1.普通函數傳參
    def func1(x:Int,y:Int) = x+y
    println(func1(1,2))
    
    //2.高階函數傳參以返回值是函數為例)
    def func2(x:Int) = (y:Int) => x+y
    //method one
    val func22=func2(2)
    println(func22(1))
    //method two
    println(func2(1)(2))
    
    //3.高階函數改寫成柯裏化方式傳入參數
    def func3(x:Int)(y:Int) = x+y
    //method one
    val func33=func3(3) _
    println(func33(2))
    //method two
    println(func3(5)(6))
    
    //4.有默認值時的調用
    def func4(x:Int)(y:Int =10) = y+x
    //method one 調用時,必須使用()()
    println(func4(5)())
    //method two
    println(func4(5)(5))
    
    //5.使用隱式參數在有默認值時的調用
    def func5(x:Int)( implicit y:Int =10) = y+x
    //method one 調用時,必須使用()()
    println(func5(5)())
    //method two
    println(func5(5)(5))
    }
    }
  • 關於方法中的隱式參數
    在聲明方法時,有某個參數使用了implicit修飾,並附加了默認值。

    object Test01 {
    def main(args: Array[String]): Unit = {
    //聲明一個有implicit修飾的參數的方法
    def sum(x:Int)(implicit  y:Int=10) =x+y
    
    //1.碼運行的全局環境中不存在一個同類型的隱式變量,而且調用的時候,也沒有參入參數
    println(sum(5))  //結果打印:15
    
    //2.如果全局環境中,存在一個同類型的隱式變量
    implicit  val num:Int=6
    println(sum(5))  //結果打印:11 ,這個全局的隱式變量值會替換方法定義中指定
    
    //3.忽略所有的隱式變量值,可以由用戶直接傳入新的參數
    println(sum(5)(5))  //結果打印:10
    }
    }

    註意:如果全局出現了多個同類型的隱式參數:

    implicit val abc:Int =6
    implicit val aaa:Int =6
    def sum(x:Int)(implicit y:Int =5) = x+y
    println(sum(5))

    此時這個段代碼會報錯:Error:(80, 16) ambiguous implicit values,所以,如果在方法中定義了隱式的參數,那麽在全局變量中只能有一個與方法中類型相同的隱式變量。
    隱式參數的總結
      - 隱式轉換會首先從全局中尋找,尋找不到,才使用隱式參數
      - 如果隱式參數存在二義性,那麽程序就報錯

  • 高階函數的總結
      - 函數是 Scala 中的頭等公民
      - 函數可以作為方法的參數
      - 函數可以作為方法的返回值
      - 方法也可以被轉換成函數,特定的場景下使用”“ 進行轉化即可

2. 閉包

 閉包是一個函數,返回值依賴與聲明在函數外部的一個或者多個變量。
 閉包通常來講可以簡單的認為是可以訪問一個函數裏面局部變量的另一個函數。
例:

object Test01 {
  def main(args: Array[String]): Unit = {
    //定義一個閉包函數
    def bibao() ={
      var num:Int=0
      //在閉包函數中定義一個函數,用於修改變量num的值
      val add=(x:Int)=>{
        num+=x
        num
      }
      //最終將這個函數返回
      add
    }
    val result=bibao()
    println(result(1))  //1
    println(result(2))  //3
    println(result(3))  //6
  }
}

 通過上面的案例我們了解到:每一次在調用result實際上是調用bibao方法中的add函數,然後對bibao方法中的num變量進行疊加,實現了使用另一個函數,訪問其他方法中的局部變量的功能,這個就是閉包。當bibao調用的時候,就會申請一塊內存區間,存儲了add和num變量,bibao這個函數當被調用的時候,就返回了內存的一個函數add,調用的時候,result(1),相當於add(2),num的值就會被返回。當然如果重新調用一次bibao方法,這個num變量會被重新初始化。
閉包的弊端 : 在一個線程中,或者一個程序中,不能太多的定義這樣的閉包函數,定義閉包函數時,其中的局部變量一定不能太大。因為閉包中的局部變量時常駐內存的,一旦定義之後,就一直在內存中,除非程序終止。

3. 隱式轉化

scala的神奇之處:之前有過1 to 10 其實可以寫成 1.to(10),那其實就是表示:1 是一個 Int 類型的變量,所以證明 Int 類中會有一個 to 的方法,但事實上,我們在 Int 類型中根本就沒有尋找 to 方法,那也就是說對一個 Int 類型的變量 1 調用 Int 類型不存在的一個方法,這怎麽還能正常運行 呢? 隱式轉換
dome01:

object Test01 {
  def main(args: Array[String]): Unit = {
    //定義一個傳入兩個Int類型的方法
    def m1(x:Int,y:Int):Int ={
      x+y
    }
    //定義一個Double的轉換成 Int類型 方法,隱式轉化引入這個方法
    implicit def m2(x:Double) = x.toInt

  //調用m1,並傳入double類型的值
    println(m1(3.5,2.6)) //沒有報錯,正常打印
  }
}

dome02:
技術分享圖片

  • 隱式轉換的種類
      在方法傳入值的時候,如果定義的參數和傳入的參數類型不同,使用隱式轉化。
    def m1(x:Int,y:Int):Int ={
    x+y
    }
    implicit  def m2(x:Double) = x.toInt
    println(m1(2.0,3.2))

      當一個類沒有某個方法的時候,但是此時調用這個方法,使用隱式轉換
    以1.to(5)這個為例,1是一個int類型,但是Int中沒有定義to方法,但是richInt中定義了to方法調用了:1 to 10,其實是調用了:1.to(10),但是:Int 中沒有 to 方法,所以:去尋找引入的隱式轉換中有沒有能把 Int 類型轉換成能執行 to 方法的類型,果然:在系統引入的轉換中發現:implicit def intWrapper(x: Int): runtime.RichInt,所以:最終 int 類型的 1 就被轉換成了 RichInt 類型的變量

  • 隱式轉換的時機
    當調用某個對象不存在的方法時
    object Test01 {
    def main(args: Array[String]): Unit = {
    //導入MyFile的任意方法
    import MyFile._
    val file:File=new File("c://a.txt")
    //調用了File的沒有的方法,而readAll是隱式轉換的RichFile的方法
    file.readAll()
    }
    }
    object MyFile{
    //將File轉換為RichFile
    implicit def m1(file:File):RichFile = new RichFile(file)
    }
    class RichFile(file:File){
    def readAll(): String ={
    Source.fromFile(file).mkString
    }
    }

    方法參數類型不匹配時

    object Test01 {
    def main(args: Array[String]): Unit = {
    import  ObjectImplicit._
    def m1(worker:Worker) =println("person:"+worker.name)
    m1(new Older("older"))
    m1(new Worker("worker"))
    m1(new Adult("adult"))
    m1(new Young("young"))
    }
    }
    class Older(val name: String)
    class Young(val name: String)
    class Worker(val name: String)
    class Adult(val name: String)
    object ObjectImplicit{
    implicit def objectworker(obj: AnyRef): Worker ={
    if(obj.getClass==classOf[Older]){
      val older=obj.asInstanceOf[Older]
      new Worker(older.name)
    }else if(obj.getClass==classOf[Young]){
      val young=obj.asInstanceOf[Young]
      new Worker(young.name)
    }else if(obj.getClass==classOf[Adult]){
      val adult=obj.asInstanceOf[Adult]
      new Worker(adult.name)
    }else if(obj.getClass==classOf[Worker]){
      val worker=obj.asInstanceOf[Worker]
      worker
    }else{
      new Worker("Null")
    }
    }
    }

Scala的函數式編程