Kotlin 入門(mén)(三):高階函數(shù)

Lambda 表達(dá)式

1.lambda 表達(dá)式總是被大括號(hào)括著
2.其參數(shù)(如果有的話(huà))在->之前聲明(參數(shù)類(lèi)型可以省略)

  1. 函數(shù)體(如果存在的話(huà))在 ->后面。
    4.在 Kotlin 中有一個(gè)約定,如果函數(shù)的最后一個(gè)參數(shù)是一個(gè)函數(shù),并且你傳遞一個(gè) lambda 表達(dá) 式作為相應(yīng)的參數(shù),你可以在圓括號(hào)之外指定它

一個(gè) lambda 表達(dá)式或匿名函數(shù)是一個(gè)“函數(shù)字面值”,即一個(gè)未聲明的函數(shù), 但立即做為表達(dá) 式傳遞??紤]下面的例子:

max(strings,    {   a,  b   ->  a.length    <   b.length    })

函數(shù) max 是一個(gè)高階函數(shù),換句話(huà)說(shuō)它接受一個(gè)函數(shù)作為第二個(gè)參數(shù)。 其第二個(gè)參數(shù)是一 個(gè)表達(dá)式,它本身是一個(gè)函數(shù),即函數(shù)字面值。寫(xiě)成函數(shù)的話(huà),它相當(dāng)于:

fun compare(a:  String, b:  String):    Boolean =   a.length    <   b.length

對(duì)于接受另一個(gè)函數(shù)作為參數(shù)的函數(shù),我們必須為該參數(shù)指定函數(shù)類(lèi)型。 例如上述函數(shù) max 定義如下:

fun <T> max(collection:Collection<T>,   less:   (T, T)->    Boolean):   T?  {
var max:    T?  =   null
for (it in  collection)
if(max  ==  null    ||  less(max,   it))    
max =   it
return  max 
}
}

參數(shù) less 的類(lèi)型是 (T, T) -> Boolean ,即一個(gè)接受兩個(gè)類(lèi)型 T 的參數(shù)并返回一個(gè)布爾值 的函數(shù): 如果第一個(gè)參數(shù)小于第二個(gè)那么該函數(shù)返回 true。

匿名函數(shù)

上面提供的 lambda 表達(dá)式語(yǔ)法缺少的一個(gè)東西是指定函數(shù)的返回類(lèi)型的 能力。在大多數(shù)情 況下,這是不必要的。因?yàn)榉祷仡?lèi)型可以自動(dòng)推斷出來(lái)。然而,如果 確實(shí)需要顯式指定,可 以使用另一種語(yǔ)法: 匿名函數(shù) 。

fun(x:  Int,    y:  Int):   Int =   x   +   y

匿名函數(shù)看起來(lái)非常像一個(gè)常規(guī)函數(shù)聲明,除了其名稱(chēng)省略了。其函數(shù)體 可以是表達(dá)式(如 上所示)或代碼塊。
匿名函數(shù)的返回類(lèi)型推斷機(jī)制與正常函數(shù)一樣:對(duì)于具有表達(dá)式函數(shù)體的匿名函數(shù)將自動(dòng) 推 斷返回類(lèi)型,而具有代碼塊函數(shù)體的返回類(lèi)型必須顯式 指定(或者已假定為Unit )。

請(qǐng)注意,匿名函數(shù)參數(shù)總是在括號(hào)內(nèi)傳遞。 允許將函數(shù) 留在圓括號(hào)外的簡(jiǎn)寫(xiě)語(yǔ)法僅適用于 lambda 表達(dá)式。

Lambda表達(dá)式和匿名函數(shù)之間的另一個(gè)區(qū)別是 非局部返回的行為。一個(gè)不帶標(biāo)簽的 return 語(yǔ)句 總是在用 fun 關(guān)鍵字聲明的函數(shù)中返回。這意味著 lambda 表達(dá)式中的 return 將從 包含它的函數(shù)返回,而匿名函數(shù)中的 return 將從匿名函數(shù)自身返回。

閉包

Lambda 表達(dá)式或者匿名函數(shù)(以及局部函數(shù)和對(duì)象表達(dá)式) 可以訪問(wèn)其 閉包 ,即在外部作 用域中聲明的變量。 與 Java 不同的是可以修改閉包中捕獲的變量:

var sum =   0 
ints.filter
{   it  >   0   }.forEach   {
sum +=  it 
} 
print(sum)

高階函數(shù)

高階函數(shù)是將函數(shù)用作參數(shù)或返回值的函數(shù)。這種函數(shù)的一個(gè)很好的例子是 lock() ,它接 受一個(gè)鎖對(duì)象和一個(gè)函數(shù),獲取鎖,運(yùn)行函數(shù)并釋放鎖:

fun <T> lock(lock:  Lock,   body:   ()  ->  T): T{
lock.lock()
try {
return  body()
}finally{
lock.unlock()
} }

讓我們來(lái)檢查上面的代碼: body 擁有函數(shù)類(lèi)型: () -> T , 所以它應(yīng)該是一個(gè)不帶參數(shù)并 且返回 T 類(lèi)型值的函數(shù)。 它在 try -代碼塊內(nèi)部調(diào)用、被 lock 保護(hù),其結(jié)果 由 lock() 函數(shù)返回。
如果我們想調(diào)用 lock() 函數(shù),我們可以把另一個(gè)函數(shù)傳給它作為參數(shù),通常會(huì)更方便的另一種方式是傳一個(gè) lambda 表達(dá)式:

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

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

內(nèi)聯(lián)函數(shù)

使用高階函數(shù)會(huì)帶來(lái)一些運(yùn)行時(shí)的效率損失:每一個(gè)函數(shù)都是一個(gè)對(duì)象,并且會(huì)捕獲一個(gè)閉 包。 即那些在函數(shù)體內(nèi)會(huì)訪問(wèn)到的變量。 內(nèi)存分配(對(duì)于函數(shù)對(duì)象和類(lèi))和虛擬調(diào)用會(huì)引入 運(yùn)行時(shí)間開(kāi)銷(xiāo)。
但是在許多情況下通過(guò)內(nèi)聯(lián)化 lambda 表達(dá)式可以消除這類(lèi)的開(kāi)銷(xiāo)。
下述函數(shù)是這種情況的 很好的例子。即lock()函數(shù)可以很容易地在調(diào)用處內(nèi)聯(lián)??紤]下面的情況:

lock(l) {   foo()   }

編譯器沒(méi)有為參數(shù)創(chuàng)建一個(gè)函數(shù)對(duì)象并生成一個(gè)調(diào)用。取而代之,編譯器可以生成以下代 碼:

l.lock() try    {
foo() 
} finally{
l.unlock()
 }

這個(gè)不是我們從一開(kāi)始就想要的嗎?
為了讓編譯器這么做,我們需要使用 inline 修飾符標(biāo)記 lock() 函數(shù):

inline  fun lock<T>(lock:   Lock,   body:   ()  ->  T): T
{               //  …… }

inline 修飾符影響函數(shù)本身和傳給它的 lambda 表達(dá)式:所有這些都將內(nèi)聯(lián) 到調(diào)用處。
內(nèi)聯(lián)可能導(dǎo)致生成的代碼增加,但是如果我們使用得當(dāng)(不內(nèi)聯(lián)大函數(shù)),它將在 性能上有 所提升,尤其是在循環(huán)中的“超多態(tài)(megamorphic)”調(diào)用處。

Lambda表達(dá)式的返回

Kotlin 有函數(shù)字面量、局部函數(shù)和對(duì)象表達(dá)式。因此 Kotlin 的函數(shù)可以被嵌套。 標(biāo)簽限制的 return 允許我們從外層函數(shù)返回。 最重要的一個(gè)用途就是從 lambda 表達(dá)式中返回。

fun foo()   {       
    ints.forEach    {   
    if  (it ==  0)
    return      
    print(it)               
} }

//從 lambda  表達(dá)式中返回,
fun foo()   {           
ints.forEach    lit@    {       
    if  (it ==  0)  
return@lit                  
print(it)               } }

//下劃線(xiàn)用于未使用的參數(shù)
map.forEach {   _,  value   ->  println("$value!")  } 

常用高階函數(shù)

1.forEach
提供了遍歷集合對(duì)象的功能,這里只查看IntArray類(lèi)的forEach方法實(shí)現(xiàn),源碼在_Arrays.kt文件中。

public inline fun IntArray.forEach(action: (Int) -> Unit): Unit {
    for (element in this) action(element)
}

// 使用
val arr = intArrayOf(1, 2, 4, 6)
arr.forEach {
    println(it)
}

2.let
它接收了調(diào)用者作為參數(shù)并且返回任意的類(lèi)型的lambda表達(dá)式,最后以自己為參數(shù)調(diào)用lambda表達(dá)式
代碼:

public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

使用:

val arr = intArrayOf(1, 2, 4, 6)
arr.let {
    var sum = 0
    // 這個(gè)it就是arr對(duì)象
    for (i in it) {
        sum += i
    }
    println(sum)
}

3.map
map就是常用映射,函數(shù)其實(shí)就是一種映射關(guān)系,將輸入的參數(shù)映射成輸出的參數(shù)值。

public inline fun <R> IntArray.map(transform: (Int) -> R): List<R> {
    return mapTo(ArrayList<R>(size), transform)
}

public inline fun <R, C : MutableCollection<in R>> IntArray.mapTo(destination: C, transform: (Int) -> R): C {
    for (item in this)
        destination.add(transform(item))
    return destination
}

//使用
val arr = intArrayOf(1, 2, 4, 6)
val newArr = arr.map { (it * 2).toString()  }
println(newArr)

4.flatMap
flatMap是一種支持二維集合映射的高階函數(shù)。

public inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R> {
    return flatMapTo(ArrayList<R>(), transform)
}

public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.flatMapTo(destination: C, transform: (T) -> Iterable<R>): C {
    for (element in this) {
        val list = transform(element)
        destination.addAll(list)
    }
    return destination
}

使用:

val arr = intArrayOf(1, 2, 4, 6)
val arr2 = intArrayOf(10, 39, 39, 18, 88)
var arr3 = intArrayOf(100, 200, 383, 198)

val newArr = arrayListOf(arr, arr2, arr3)
val flatArr = newArr.flatMap {
    iterator -> iterator.map {
        it.toString()
    }
}

5.filter
對(duì)集合里的元素做過(guò)濾操作,只有那些符合要求的對(duì)象才需要用戶(hù)做處理

public inline fun IntArray.filter(predicate: (Int) -> Boolean): List<Int> {
    return filterTo(ArrayList<Int>(), predicate)
}

public inline fun <C : MutableCollection<in Int>> IntArray.filterTo(destination: C, predicate: (Int) -> Boolean): C {
    for (element in this) if (predicate(element)) destination.add(element)
    return destination
}

使用:

val arr = intArrayOf(1, 2, 4, 6, 10, 39, 39, 18, 88)
val newArr = arr.filter { it % 2 == 0 }
println(newArr)

// 輸出結(jié)果
// [2, 4, 6, 10, 18, 88]

6.takeWhile
takeWhile和filter一樣都是過(guò)濾用的函數(shù),它的實(shí)現(xiàn)和filter不同地方在filter總是會(huì)遍歷當(dāng)前IntArray的所有元素,而takeWhile在第一次發(fā)現(xiàn)predict不滿(mǎn)足的時(shí)候就不再遍歷,后面的元素即使?jié)M足條件也不會(huì)加入到結(jié)果中。

public inline fun IntArray.takeWhile(predicate: (Int) -> Boolean): List<Int> {
    val list = ArrayList<Int>()
    for (item in this) {
        if (!predicate(item))
            break
        list.add(item)
    }
    return list
}

使用:

val arr = intArrayOf(1, 2, 4, 6, 10, 39, 39, 18, 88)
val newArr = arr.takeWhile { it % 2 == 0 }
println(newArr)

// 輸出結(jié)果為空,因?yàn)榈谝粋€(gè)1不是偶數(shù),直接返回,沒(méi)有任何結(jié)果
// []

7.take/takeLast
take是從集合中取前幾個(gè)元素,takeLast是從集合中取后幾個(gè)元素。

public fun IntArray.take(n: Int): List<Int> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    if (n == 0) return emptyList()
    if (n >= size) return toList()
    if (n == 1) return listOf(this[0])
    var count = 0
    val list = ArrayList<Int>(n)
    for (item in this) {
        if (count++ == n)
            break
        list.add(item)
    }
    return list
}

public fun IntArray.takeLast(n: Int): List<Int> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    if (n == 0) return emptyList()
    val size = size
    if (n >= size) return toList()
    if (n == 1) return listOf(this[size - 1])
    val list = ArrayList<Int>(n)
    for (index in size - n .. size - 1)
        list.add(this[index])
    return list
}

使用:

val arr = intArrayOf(1, 2, 4, 6, 10, 39, 39, 18, 88)

// [1, 2]
println(arr.take(2))

// [18, 88]
println(arr.takeLast(2))

8.fold
把集合里的所有元素結(jié)合成一個(gè)值。fold顧名思義就是折疊起來(lái),不過(guò)它會(huì)提供一個(gè)初始值。

public inline fun <R> IntArray.fold(initial: R, operation: (acc: R, Int) -> R): R {
    var accumulator = initial
    for (element in this)
        accumulator = operation(accumulator, element)

    return accumulator
}

使用:

val arr = intArrayOf(1, 2, 4, 6, 10, 39, 39, 18, 88)
arr.fold(2) { product, element ->
    product * element
}

9.reduce
reduce也就是規(guī)約的意思,也是把多個(gè)值融合成一個(gè)值的操作,不過(guò)它并不會(huì)提供一個(gè)初始值。

public inline fun IntArray.reduce(operation: (acc: Int, Int) -> Int): Int {
    if (isEmpty())
        throw UnsupportedOperationException("Empty array can't be reduced.")
    var accumulator = this[0]
    for (index in 1..lastIndex) {
        accumulator = operation(accumulator, this[index])
    }
    return accumulator
}

使用:

val arr = intArrayOf(1, 2, 4, 6, 10, 39, 39, 18, 88)
arr.reduce { product, element ->
    product * element
}

10.apply
apply用于在lambda表達(dá)式里切換上下文的高階函數(shù)

public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

使用:

class DbConfig {
    var url: String = ""
    var username: String = ""
    var password: String = ""

    override fun toString(): String {
        return "url = $url, username = $username, password = $password"
    }
}

class DbConnection {
    fun config(conf: DbConfig) {
        println(conf)
    }
}

fun main(args: Array<String>) {
    val conn = DbConnection()
    conn.config(DbConfig().apply {
        url = "mysql://127.0.0.1:3306/hello"
        username = "root"
        password = "123456"
    })
}

11.with
ith可以讓用戶(hù)省略點(diǎn)號(hào)之前的對(duì)象引用,with內(nèi)部的所有操作都是針對(duì)with對(duì)象

public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

使用:

class MyLogger {
    var tag: String = "TAG"

    fun e(msg: String) {
        println("$tag  $i")
    }

    fun tag(tagStr: String) {
        tag = tagStr
    }
}

fun main(args: Array<String>) {
    val logger = MyLogger()
    with(logger) {
        tag("Kotlin")
        e("It is a good language")
    }
}

12.use
針對(duì)那些實(shí)現(xiàn)了Closable接口的對(duì)象的擴(kuò)展方法,也就是大部分的IO操作相關(guān)類(lèi)會(huì)有這個(gè)擴(kuò)展高階方法,查看它的源代碼在Closable.kt源文件中。

public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
    var exception: Throwable? = null
    try {
        return block(this)
    } catch (e: Throwable) {
        exception = e
        throw e
    } finally {
        when {
            apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
            this == null -> {}
            exception == null -> close()
            else ->
                try {
                    close()
                } catch (closeException: Throwable) {
                    // cause.addSuppressed(closeException) // ignored here
                }
        }
    }
}

使用:

val file = File("test.txt")
val bufferReader = BufferedReader(FileReader(file))
bufferReader.use {
    it.readLine()
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容