2019年7月2日 星期二

理解 Kotlin 的高階函式

參考文章: https://kotlinlang.org/docs/reference/lambdas.html
Kotlin 是一個 typesafe 程式語言, 每個變數都必須需確認他的資料型態(type), 函式的名稱可以看成是常數(變數), 因此函式也不例外需要加以宣告. 程式資料型態(function type)可以用成對小括號加上箭頭符號組成 ( ) -> , 用 typealias 定義函式型態後可以讓程式碼變得簡單易讀, 同時也能重複利用. 定義函式時若將函式當作輸入參數或是輸出函數,他就是所謂的高階函式 :
typealias  funit =  ( ) -> Unit   // 函式資料型態: 無輸入參數, 也不傳回任何數值
fun g(f: funit) { // f 是一個輸入函式, 因此 g(f) 是高階函式,  f 可以在函式 g 裏面執行
    println("Run in HighOrderFunction")
    f( ) // 輸入的函式在此開始執行
}
fun main() {
    val  f: funit =  {  println("Hello")        }  // f 指定成 λ 函式, 符合"無輸入參數,無傳回值"
    g( f ) // 把函式丟進高階函式裏面去執行,
}
有一種有趣的高階函式:輸入參數是一個攜帶著物件類型的函式(lambda with Receiver), 透過綁定物件的手法, 讓呼叫的方法就好像侷限(scope)在物件裏面執行(context)一樣:
        class Classname( ) {
              fun doMethod( ) {
                    println("This is a method")
              }
        }
        fun HighOrderλwithReceiver(λ: Classname.( ) -> Unit) {
             val objectContext =  Classname( )            
             objectContext.λ ( )
        } 
        fun main( ) { 
               // fun doMethod { println("Global") }
                HighOrderλwithReceiver  {       
                        // fun doMethod { println("Local") }                   
                       doMethod( )   // this.doMethod( )
                }
        }
上述當呼叫 HighOrderλwithReceiver 函式時, 因為最後一項參數是 λ 函式, 所以將它搬離出小括號, 搬離後剛好沒有參數, 小括號也跟著不用寫, 這是 Kotlin 語言的特異功能. 傳進來的 λ 區塊 {   } , 在裏面的變數若沒有指定物件的範疇(scope)時, 編譯器就會在當地區域(Local scope),物件的本體區域(context 也就是 this), 及整體區域(Global scope) 推測(infer)並編譯(compile)出適當的變數或函數, 因為在高階函式 HighOrderλwithReceiver 的定義裏面, 已經預先設定進來的函式屬於 Classname 類型的一種方法, 就好像是先築起一道牆(scope), 利用類型建立起物件副本(instance)也就是物件文本(context),接著在物件的範疇下執行 λ 裏面的每一道程序, 因此 λ 區塊裏面, 呼叫的函式若是 Classname 裡面的從屬函數的話, this 就可以省略不寫了. 這是 Function literals with receiver 厲害的地方.

沒有留言: