Kotlin研發第十三彈——高階函數和lambda表達式

高階函數和lambda表達式

尾遞歸函數(tailrec)

kotlin支持函數時編程的尾遞歸。這個允許一些算法可以通過循環而不是遞歸解決問題,從而避免了棧溢出。當函數被標記爲tailrec時,編譯器會優化遞歸,並用高效迅速的循環代替它

//尾遞歸
tailrec fun findFixPoint(x:Double=1.0):Double=if (x==Math.cos(x))x else findFixPoint(Math.cos(x))

這是計算餘弦不動點,輸出結果

0.7390851332151607

**tailrec:**符號使用後,最後一個操作只能是調用函數自己,

不允許在遞歸代碼後不允許有其他代碼的

不允許在try/catch/finally塊中

當前的尾遞歸只能在JV買的後端中用

高階函數 ?

高階函數就是可以接受函數作爲參數或者返回一個函數的函數。

比如lock()就是例子,接受一個lock對象和一個函數,運行函數並釋放lock

//高階函數
fun <T> lock(lock: Lock,body:()->T):T{
    lock.lock()
    try {
        return body()
    }
    finally {
        lock.unlock()
    }
}

如果我們想調用lock()函數,我們可以傳給他另外一個函數作爲參數

fun toBeSynchroized() = sharedResource.operation()
val result = lock(lock, ::toBeSynchroized)

其中最方便的辦法是傳遞一個字面函數(通常爲lambda表達式):

val result=lock(lock,{sharedResource.operation()})

字面函數

字面函數被包括在大括號裏

參數在 ->前面聲明(參數類型可以省略)

函數體在->之後

在kotlin中有一個約定,如果某一個函數的最後一個參數是函數,並且你向那個位置傳遞一個lambda表達式,那麼你可以在括號外面定義這個lambda表達式
lock(lock){
    sharedResource.operation()
}
map()(ofMapReduce):
fun <T,R> List<T>.map(transform:(T)->R):List<R>{
    val result= arrayListOf<R>()
    for (item in this){
        result.add(transform(item))
    }
    return result
}

 val doubled= arrayListOf(1,2,3).map { it->it*2 }
    println(doubled)

如果字面只有一個參數,聲明可以省略,名字叫就是it

val doubled= arrayListOf(1,2,3).map { it*2 }
    println(doubled)

輸出結果

[2, 4, 6]

並且可以寫LINQ-風格的代碼

val strings= arrayOf("a","abc","abcd","qww")
    val test=strings.filter { it.length==3 }.sortedBy { it }.map { it.toUpperCase() }
    for (ite in test){
        println(ite)
    }

輸出

ABC
QWW

內聯函數

**內聯函數時爲了提高高階函數的性能:**使用高階函數會帶來一些運行時的效率損失:每一個函數都是一個對象,且這個對象捕獲了一個閉包,閉包內的變量可以在函數對象內部訪問。內存分配(爲函數對象和類)和實際調用將引入運行時間開銷。

但通過使用內聯lambda表達式,可以避免這些情況出現。

內聯函數如何運作

當一個函數被聲明inline時,它的函數體是內聯的,也就是說,函數體會被直接替換到函數調用的地方,如下例子:

//內聯函數
inline fun inlineTest(prefix:String,action:()->Unit){
    println("call before $prefix")
    action()
    println("call after $prefix")
}

 //內聯函數
    inlineTest("douwowan"){
        println("hahhah")
    }

輸出

call before douwowan
hahhah
call after douwowan

上面所述代碼就相當於直接輸出——編譯器編譯時顯示成這樣:

 println("call before douwowan")
 action()
 println("call after douwowan")
高階函數的方式——傳遞函數類型的變量作爲參數
val call:()->Unit={ println("hahaha")}
inlineTest("都一樣",call)
inlineTest("差不多",{println("hahaha")})

輸出

call before 都一樣
hahaha
call after 都一樣
call before 差不多
hahaha
call after 差不多

參數

函數參數是用Pascal符號定義的 name:type。參數之間用都好隔開,每個參數必須指明類型。

fun powerOf(number:Int,exponent:Int){}

默認參數

函數參數可以設置默認值,當參數被忽略時會使用默認值,

這樣比其他語言可以減少重載
fun read(b:Array<Byte>,off:Int=0,len:Int=b.size){}

默認值可以通過type類型後使用=進行賦值

命名參數

在調用函數時可以參數命名,這對於那種有大量參數的函數很方便

 fun reformat(str:String,isMale:Boolean=true,age:Int=27){...}
 TestResourceLoader().reformat("wyc")

使用時可以使用默認的參數來初始化方法不用過分重載,適用於某些含有默認屬性的方法

注意:命名參數語法不能用於調用Java函數中,因爲Java的字節碼不能確保方法參數命名的不變性

不帶返回值的參數

如果函數不會反悔任何有用值,那麼他的返回類型就是unit.並且可以省略

fun printHello(name: String?): Unit {
    if (name != null)
        println("Hello ${name}")
    else
        println("Hi there!")
    // `return Unit` or `return` is optional
}

單表達式函數

當函數只返回單個表達式,大括號可以省略並在=後面定義函數體

fun double(x:Int):Int=X*2

在編譯器可以推斷出返回值類型的時候,返回值可以省略

fun double(x:Int)=X*2

明確返回類型

下面的例子中必須有明確返回類型,除非他是返回Unit類型。kotlin並不會對函數體重的返回類型進行推斷,因爲函數體中可能有複雜的控制流,他的返回類型未必對讀者課件(甚至編譯器而言也有可能是不可見的)

變長參數

函數的參數(通常是最後一個參數)可以用vararg修飾符進行標記:

//可變長度參數
fun <T> asList(vararg ts:T):List<T>{
    val result=ArrayList<T>()
    result.addAll(ts)
    return result
}
println(asList(1,2,3,"wyc"))

輸出結果

[1, 2, 3, wyc]

標記後允許傳遞可變長度的參數

注意,只有一個參數可以被標註vararg。加入vararg並不是列表中的最後一個參數,那麼後面的參數需要通過命名參數語法進行傳值,再或者如果這個參數是函數類型,就需要通過lambda法則。

當調用變長參數的函數時,我們可以一個一個傳遞參數,比如asList(1,2,3),或者我們傳遞一個array的內容給函數,我們就可以使用*前綴操作符:

 val list= arrayOf("q","w","e","r")
    val changeResult= asList(1,2,3,"wyc",*list)
    println(changeResult)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章