1. 程式人生 > 其它 >【Scala】03 函式

【Scala】03 函式

1、Scala的方法語法:

object Hello {
  def main(args : Array[String]) : Unit = {

    // scala 允許在方法的宣告中再宣告方法,並且呼叫
    def someFunction(x : Int, y : String): Unit = {
      println("this function has been called! (In this main method)")
    }

    // 呼叫方法中宣告的
    someFunction(100, "str")

    // 呼叫這個物件宣告的
    val returnVal = Hello.someFunction(2, "ol")
  }

  
// 在物件中也可以宣告方法,支援和方法中重名 def someFunction(x : Int, y : String): Int = { println("this function has been called! (In this single instance)") return 100 } }

2、引數和返回值

    // 沒有引數 沒有返回值
    def method01(): Unit = {
      print("method01")
    }
    // 有引數 沒有返回值
    def method02(x : Int, y : String): Unit = {
      print(
"method02") } // 有引數 有返回值 def method03(x : Int, y : String): Int = { print("method01") return 100 } // 沒有引數 有返回值 def method04(): Int = { return 300 }

3、引數特性

// 在引數型別後面加上*號表示該引數是可變引數
def method(str : String*): Unit = {
  println(str)
}
method("hello", "scala") // 列印結果是一個數組序列 ArraySeq(hello, scala)

// 多個引數必須將可變引數放在最後一個 def method2(x : Int, y : Double, string: String*): Unit = { } // 預設引數 def method3(name : String = "這是預設值"): Unit = { println(name) } method3() // 這樣無參也可以被呼叫,因為已經設定了一個預設值在這裡 C++是支援這種操作的 method3("jojo") // 帶名引數,這個引數的意思是,你可以在呼叫的時候強制指定注入的引數是給哪個引數的 def method4(name : String, age : Int, gender : Boolean): Unit = { } // 帶名引數的意義就在於支援打亂順序入參 method4(gender = true, name = "張三", age = 30)

4、函式特性:

// Lambda表示式特性

// 1、可以省略return,Scala支援 函式體的最後一段程式碼最為返回的結果
def f0(str : String) : String = {
  str // 這裡就省略的了 return
}
println(f0("param"))

// 2、函式體只有一段程式碼可以省略闊話表示
def f1(str : String) : String = str

// 3、如果型別可以推斷出來,則不需要強制宣告返回型別是什麼
def f2(str : String) = str

// 4、寫了Return就必須宣告返回型別
def f3(str : String) : String = return str

// 5、明確了返回型別為Unit,則 return沒有任何意義
def f4(str : String) : Unit = return str // 這個str無意義

// 6、Unit返回型別 可以直接向Java一樣宣告
def f5(str : String) {
  println(str)
}

// 7、沒有引數,但是宣告的時候寫了,呼叫可以沒有括號,加括號也行
def f6() {
  println("str")
}
f6() // 這樣呼叫正常
f6 // 這樣呼叫也沒問題

// 8、如果宣告的時候就沒有引數列表,呼叫絕不能寫括號
def f7 {
  println("str")
}
f7 // 呼叫正常
// f7() // 這樣呼叫編譯報錯

// 如果不關心名稱,只需要邏輯處理,這裡也可以匿名宣告
(name : String) => {
  println(name)
}

5、函式可以作為引數注入

    val funcRef = (string : String) => {
      println(string)
    }

    // 定義一個函式,引數可以是一個函式
    def funcForFunc(function: String => Unit): Unit = {
      function("asdas");
    }

    // 這裡很奇怪的一點是,引數為什麼可以在裡面注入,而外面只需要宣告入引數函式?
    funcForFunc(funcRef)

    // 我有點想明白了,這樣的話,引數是頂死的,但是允許套用不同的方法,只要方法符合這個引數的方法型別要求即可被套用

    // 語法縮寫

    // 1、引數型別可以省略不寫,只要能夠被推匯出來
    funcForFunc((name) => {println(name)})

    // 2、引數只有一個可以不寫引數括號
    funcForFunc(name => {println(name)})

    // 3、執行的語句只有一行可以不寫花括號表示
    funcForFunc(name => println(name))

    // 4、引數只使用過一次的情況,可以不宣告引數了,直接使用下劃線表示
    funcForFunc(println(_))

    // 5、最後是呼叫推斷
    funcForFunc(println)

用法案例介紹:

    // 定義一個二元運算的函式,只操作固定的1和2個值,具體的運算由注入的函式實現
    def calcForThis(fun : (Int, Int) => Int) : Int = {
      fun(100, 200)
    }

    val sum = (a : Int, b : Int) => a + b
    val minus = (a : Int, b : Int) => a - b
    val max = (a : Int, b : Int) => if (a > b) a else b
    val min = (a : Int, b : Int) => if (a > b) b else a


    // 呼叫
    val s = calcForThis(sum)
//    val s = calcForThis(minus)
//    val s = calcForThis(max)
//    val s = calcForThis(min)

    // 匿名函式簡化

    // 原始完整呼叫
    println(calcForThis((a : Int, b : Int) => a + b))
    // 對匿名簡化
    println(calcForThis((a, b) => a + b))
    // 引數簡化
    println(calcForThis( _ + _))

這和Java或者之前學習的程式設計不太一樣

我們建立各種方法,是為了對引數進行處理,方法是為了封裝邏輯

現在感覺是反過來,引數是既定的,封裝的成方法了,這個感覺寫起來像那個介面的感覺一樣

6、函式的進一步操作

    // 1、在函式體中宣告函式,然後又呼叫函式
    def foo() {
      println("foo ...")
    }
    foo()

    // 2、作為值進行傳遞
    def foo2() : Int =  {
      100
    }
    val res = foo2()  // 將函式的返回值傳遞給 res
    val res2 = foo2() _ // 將函式自己作為值傳遞給 res2
    val res3:() => Int = foo2 // 如果明確變數型別? 可以不使用下劃線將函式作為整體傳遞給引數


    // 3、函式入參 作為函式引數
    def subjectFunction(paramFunction : (Int, Int) => Int): Int = {
      paramFunction(100, 314) // subjectFunction 主體函式, paramFunction引數函式
    }
    subjectFunction(_ + _)
    subjectFunction(_ * _)

    // 4、函式作為返回值返回
    def f1() = { // 宣告外層一個f1函式
      def f2() = { // 在內層中宣告一個f2函式

      }
      f2 _ // 並且返回f2這個函式
    }
    val s = f1() // 呼叫就是把f2函式傳遞給s

7、對集合的一些處理操作

    // 對陣列進行操作,如何操作作為一個抽象來定義,處理完畢返回一個新的陣列
    def arrOp(arr : Array[Int], op : Int => Int) : Array[Int] = {
      for (elem <- arr) yield op(elem)
    }

    // 宣告一個加一操作
    def increaseOne(elem : Int) = {
      elem + 1
    }

    // 使用
    val sourceArr = Array(1, 10, 30, 100) // 宣告一個原始的陣列
    val newArr = arrOp(sourceArr, increaseOne) // 注意這裡給方法的時候是把方法本身傳遞,而不是呼叫
    println(newArr.mkString(","))

    // 簡化呼叫
    val newArr2 = arrOp(sourceArr, _ + 1) 

8、案例練習:

    // 宣告匿名函式給fun變數
    val fun = (a : Int, a2 : String, a3 : Char) => {
      if(a == 0 && a2 == "" && a3 == '0') false
      else true
    }

    // 呼叫
    println(fun(0, "", '0'))

9、初見柯里化

    // 但是這段宣告的語法看不太懂
    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
    }

    // 簡寫
    def func2(i: Int): String => (Char => Boolean) = {
      (s : String)  => {
        (c : Char) => {
          if(i == 0 && s == "" && c == '0') false else true
        }
      }
    }
    // 最外層有宣告引數型別,也可以被省略
    def func3(i: Int): String => (Char => Boolean) = {
      s => c => if(i == 0 && s == "" && c == '0') false else true
    }
    // 柯里化?
    def func4(i: Int)(s: String)(c : Char):Boolean = {
      if(i == 0 && s == "" && c == '0') false else true
    }

    // 這裡肯定看不懂
    println(func(0)("")('0'))

    // 這樣來拆解出來 是這樣呼叫的,只是沒有變數接受,直接匿名進行呼叫了
    val resultFunction1 = func(0)
    val resultFunction2 = resultFunction1("")
    val res = resultFunction2('0')
    println(res)

10、案例:

    // 閉包 & 柯里化

    // 閉包概念 : 一個函式,訪問了外部的區域性變數的值,這個函式和所在的範圍稱為閉包
    // 意義在於外部函式知曉完畢出棧之後,需要保留部分的引數內容,給內部函式進行計算和呼叫

    // 案例 將固定加數作為另一個引數傳入,但是是作為第一層引數傳入
    def addByParam():Int => Int = {
      val a = 4
      def addByPa2(b : Int): Int = {
        a + b
      }
      addByPa2
    }

    def addByParam2(a:Int):Int => Int = {
      def addByPa2(b : Int): Int = {
        a + b
      }
      addByPa2
    }

    def addByParam3(a:Int):Int => Int = a + _
    // 柯里化 把一個引數列表的多個引數,變成多個引數列表
    val res = addByParam2(39)(42)

    def addCurrying(a : Int)(b : Int) = {
      a + b
    }
    addCurrying(39)(42)

11、遞迴 Recursive

    /**
     * Scala的遞迴
     * 1、方法呼叫的是自身
     * 2、必須存在可以結束的邏輯
     * 3、引數應該有規律
     * 4、遞迴必須宣告返回型別
     */
    // 階乘案例
    def factorial(n : Int): Int = {
      if (n == 0) return 1
      factorial(n - 1) * n
    }

    // 尾遞迴
    def tailFact(n : Int)= {
      def loop(n : Int, currRes : Int) : Int = {
        if (n == 0) return currRes
        loop(n - 1, currRes * n)
      }
      loop(n, 1)
    }

12、抽象控制

    // 控制抽象

    /**
     * 這裡演示一段Java寫法
     */
    val a = 100 // 宣告一個常量
    def someFunction(s : Int) = { // 宣告函式
      println(s)
    }
    someFunction(100) // 一般來說就是直接注入實際可見的值
    someFunction(a) // 或者是變數

    /**
     * Scala 希望不出現這些變數和字面值
     * 因為函式代表了一切內容
     */
    def returnTen() = 10
    someFunction(returnTen()) // 要以這種方法來進行入參的表達方式,就是控制抽象

13、傳名引數

    // 傳名引數,強調的是引數是一段程式碼塊,執行包括程式碼塊中的內容
    def fun(a : => Int) = {
      println(s"a : ${a}")
      println(s"a : ${a}")
    }

    def f1() = {
      println("f1 called")
      12
    }

    // 使用
    fun(f1()) // f1被呼叫了兩遍 a即表示函式,出現一次呼叫一次

    fun(23) // 為什麼可以傳遞23?

    /**
     * 因為23就是一個函式的返回
     */
    () => 23

14、實現自定義迴圈的案例

    // 實現自定義迴圈
    def customWhile(condition : Boolean):(=> Unit)=> Unit= {
      def doCustomLoop (operate : => Unit): Unit = {
        if (condition) {
          operate
          customWhile(condition) (operate)
        }
      }
      doCustomLoop _
    }

    // 柯里化表達
    def customWhile2(condition: => Boolean)(operate: => Unit): Unit = {
      if (condition) {
        operate
        customWhile2(condition)(operate)
      }
    }

    var n = 10
    customWhile2(n > 0) {
      println(n)
      n -= 1
    }

15、懶載入處理

    val sum = (a : Int,b : Int) => {
      println("sum has been called")
      a + b
    }
    val minus = (a : Int,b : Int) => {
      println("minus has been called")
      a - b
    }

    val result1 : Int = minus(11, 31)
    // 惰性載入
    lazy val result2 : Int = sum(11, 31)

    println("- - - 函式應該被呼叫了 - - -")
    println(s"函式應該被呼叫了 ${result2} ${result1}")