1. 程式人生 > >Scala的函數語言程式設計

Scala的函數語言程式設計

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:
Scala的函數語言程式設計

  • 隱式轉換的種類
      在方法傳入值的時候,如果定義的引數和傳入的引數型別不同,使用隱式轉化。
    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")
    }
    }
    }