15-bo‘lim

Lambda va qamrov funksiyalari

Lambda yozuvi, funksiya turlari, yuqori tartibli funksiyalar, funksiya havolalari va let, run, with, apply, also orasidagi farq.

🕑 14 daqiqa o‘qish 📄 715 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Lambda
  2. Funksiya turlari
  3. Yuqori tartibli funksiyalar
  4. Funksiya qaytarish
  5. Yopilma
  6. Funksiya havolalari
  7. Qamrov funksiyalari
  8. let
  9. apply - sozlash uchun
  10. also - yon ta'sir uchun
  11. run va with
  12. Qaysi birini tanlash
  13. inline haqida qisqacha
  14. Xulosa

Kotlin da funksiya - oddiy qiymat. Uni o'zgaruvchida saqlash, parametr sifatida uzatish va qaytarish mumkin.

Lambda #

KOTLIN
fun main() {
    val kvadrat = { x: Int -> x * x }
    val qoshish = { a: Int, b: Int -> a + b }
    val salom = { println("Salom!") }

    println(kvadrat(9))
    println(qoshish(15, 27))
    salom()
}
Natija
81
42
Salom!

Shakli: { parametrlar -> tana }. Oxirgi ifoda qaytariladigan qiymat bo'ladi - return yozilmaydi.

Funksiya turlari #

KOTLIN
fun main() {
    val kvadrat: (Int) -> Int = { it * it }
    val qoshish: (Int, Int) -> Int = { a, b -> a + b }
    val chiqar: (String) -> Unit = { println(">> $it") }
    val tasodifiy: () -> Int = { 42 }

    println(kvadrat(5))
    println(qoshish(2, 3))
    chiqar("matn")
    println(tasodifiy())
}
Natija
25
5
>> matn
42

(Int, Int) -> Int - "ikkita Int oladi va Int qaytaradi". Tur yozilgan bo'lsa, lambda ichida parametr turlarini takrorlash shart emas.

Bitta parametr bo'lsa, unga it deb murojaat qilinadi.

Yuqori tartibli funksiyalar #

KOTLIN
fun bajar(a: Int, b: Int, amal: (Int, Int) -> Int): Int = amal(a, b)

fun takrorla(marta: Int, ish: (Int) -> Unit) {
    for (i in 1..marta) ish(i)
}

fun main() {
    println(bajar(6, 7) { x, y -> x * y })
    println(bajar(6, 7) { x, y -> x + y })

    takrorla(3) { println("$it-qadam") }
}
Natija
42
13
1-qadam
2-qadam
3-qadam
Oxirgi parametr lambda bo'lsa - qavsdan tashqariga

Yuqorida bajar(6, 7) { x, y -> x * y } deb yozildi, bajar(6, 7, { ... }) emas.

Kotlin qoidasi: oxirgi parametr funksiya bo'lsa, lambda ni qavsdan keyin yozish mumkin. Lambda yagona parametr bo'lsa, qavsni butunlay tushirib qoldirish mumkin: takrorla { }.

Aynan shu qoida tufayli listOf(1,2,3).filter { it > 1 } shunday tabiiy ko'rinadi - va o'z funksiyangizni ham shunday qilib yozishingiz mumkin. Funksiya parametrini oxirgi o'ringa qo'ying.

Funksiya qaytarish #

KOTLIN
fun kopaytiruvchi(son: Int): (Int) -> Int = { it * son }

fun tekshiruvchi(chegara: Int): (Int) -> Boolean = { it > chegara }

fun main() {
    val ikkilantir = kopaytiruvchi(2)
    val uchlantir = kopaytiruvchi(3)

    println(ikkilantir(10))
    println(uchlantir(10))

    val kattami = tekshiruvchi(50)
    println(listOf(20, 60, 45, 80).filter(kattami))
}
Natija
20
30
[60, 80]

kopaytiruvchi(2) yangi funksiya yasab beradi. Bu funksiya son qiymatini eslab qoladi - bunga yopilma (closure) deyiladi.

Yopilma #

KOTLIN
fun sanagichYasa(): () -> Int {
    var soni = 0
    return { soni++; soni }
}

fun main() {
    val sana = sanagichYasa()
    println(sana())
    println(sana())
    println(sana())

    val boshqa = sanagichYasa()
    println(boshqa())
}
Natija
1
2
3
1

soni o'zgaruvchisi funksiya tugagandan keyin ham yashab qoldi - lambda uni ushlab turibdi. Har bir chaqiruv esa o'z nusxasini oladi.

Java dan farq

Java da lambda faqat final o'zgaruvchini ushlay oladi - uni o'zgartirib bo'lmaydi.

Kotlin da esa var ni ham ushlash va o'zgartirish mumkin, yuqoridagi misoldagidek. Bu qulayroq, lekin ehtiyot bo'ling: ko'p oqimli dasturda bunday o'zgaruvchi muammo manbaiga aylanadi.

Funksiya havolalari #

KOTLIN
fun juftmi(x: Int): Boolean = x % 2 == 0

fun main() {
    val sonlar = listOf(1, 2, 3, 4, 5, 6)

    println(sonlar.filter(::juftmi))
    println(sonlar.filter { juftmi(it) })

    val ismlar = listOf("malika", "aziza")
    println(ismlar.map(String::uppercase))
    println(ismlar.map(String::length))

    val chiqar: (String) -> Unit = ::println
    chiqar("havola orqali")
}
Natija
[2, 4, 6]
[2, 4, 6]
[MALIKA, AZIZA]
[6, 5]
havola orqali

::nom - mavjud funksiyaga havola. Tur::metod - o'sha turning metodiga.

Ortiqcha yuklangan funksiyaga havola

Oxirgi misolda tur aniq yozilgan: val chiqar: (String) -> Unit = ::println.

Usiz kompilyator xato beradi, chunki println ning o'nga yaqin versiyasi bor - Int, Double, Any? va boshqalar uchun - va qaysi birini olishni bilmaydi.

Qoida: funksiyaning bitta versiyasi bo'lsa, turni yozish shart emas; bir nechtasi bo'lsa - kutilayotgan turni ko'rsating.

Qamrov funksiyalari #

Kotlin da beshta maxsus funksiya bor. Ular obyekt ustida blok bajaradi va bir-biridan ikki narsa bilan farq qiladi: obyektga qanday murojaat qilinadi va nima qaytariladi.

FunksiyaObyektQaytaradi
letitBlok natijasi
runthisBlok natijasi
withthisBlok natijasi
applythisObyektning o'zi
alsoitObyektning o'zi

let #

KOTLIN
fun main() {
    val ism: String? = "Malika"

    ism?.let {
        println("Uzunlik: ${it.length}")
        println("Katta: ${it.uppercase()}")
    }

    val uzunlik = ism?.let { it.length * 2 } ?: 0
    println(uzunlik)

    val sonlar = listOf(1, 2, 3)
    println(sonlar.map { it * 2 }.let { "Natija: $it" })
}
Natija
Uzunlik: 6
Katta: MALIKA
12
Natija: [2, 4, 6]

let - eng ko'p null tekshiruvi bilan ishlatiladi va zanjirning oxirida natijani o'zgartirish uchun.

apply - sozlash uchun #

KOTLIN
class Sozlama {
    var til: String = "uz"
    var mavzu: String = "yorug'"
    var avtoSaqlash: Boolean = false

    override fun toString() = "$til/$mavzu/$avtoSaqlash"
}

fun main() {
    val s = Sozlama().apply {
        til = "en"
        mavzu = "tungi"
        avtoSaqlash = true
    }

    println(s)

    val royxat = mutableListOf<Int>().apply {
        add(1)
        add(2)
        add(3)
    }
    println(royxat)
}
Natija
en/tungi/true
[1, 2, 3]

apply obyektni qaytaradi - shuning uchun yaratish va sozlashni bitta ifodada qilish mumkin. Blok ichida this yozish shart emas.

also - yon ta'sir uchun #

KOTLIN
fun main() {
    val natija = listOf(3, 1, 2)
        .also { println("Boshlang'ich: $it") }
        .sorted()
        .also { println("Saralangan: $it") }
        .map { it * 10 }
        .also { println("Ko'paytirilgan: $it") }

    println("Yakuniy: $natija")
}
Natija
Boshlang'ich: [3, 1, 2]
Saralangan: [1, 2, 3]
Ko'paytirilgan: [10, 20, 30]
Yakuniy: [10, 20, 30]

also - zanjirni buzmasdan oraliq natijani ko'rish uchun ideal. Xatoni topishda juda qulay.

run va with #

KOTLIN
class Hisob(val egasi: String, val qoldiq: Int)

fun main() {
    val h = Hisob("Husanboy", 500000)

    val tavsif = h.run {
        "$egasi: $qoldiq so'm"
    }
    println(tavsif)

    val natija = with(h) {
        val soliq = qoldiq / 100
        "Soliq: $soliq"
    }
    println(natija)

    val qiymat = run {
        val a = 10
        val b = 20
        a + b
    }
    println(qiymat)
}
Natija
Husanboy: 500000 so'm
Soliq: 5000
30

run va with deyarli bir xil. Farq shaklda: h.run { } va with(h) { }. Birinchisi null bilan ham ishlaydi (h?.run { }).

Qaysi birini tanlash #

Qaysi qamrov funksiyasi kerak let null tekshiruvi, natijani o'zgartirish apply obyektni yaratib, sozlab olish also zanjirni buzmasdan kuzatish run obyekt ustida hisoblab, natija olish with run bilan bir xil, boshqa shakl Obyektni qaytarsa - apply/also Natijani qaytarsa - let/run/with Shubhalansangiz: sozlash uchun apply, qolgan hammasi uchun let Ichma-ich ishlatmang - it va this chalkashib ketadi
Beshtasini yodlash shart emas - ikkitasi aksariyat holatni qoplaydi
Ichma-ich qamrov funksiyalari

Ikkita let ni ichma-ich yozsangiz, ikkala blokda ham it bo'ladi va qaysi biri qaysi ekanini aytish qiyinlashadi.

Bunday holatda parametrga nom bering:

KOTLIN
foydalanuvchi?.let { f ->
    f.manzil?.let { m ->
        println("${f.ism}: ${m.shahar}")
    }
}

Yoki umuman qamrov funksiyasisiz, oddiy if bilan yozing - u ko'pincha o'qishliroq bo'ladi.

inline haqida qisqacha #

KOTLIN
inline fun olcha(nom: String, ish: () -> Unit) {
    val boshi = System.nanoTime()
    ish()
    val vaqt = System.nanoTime() - boshi
    println("$nom: ${vaqt > 0}")
}

fun main() {
    olcha("sikl") {
        var s = 0
        for (i in 1..1000) s += i
        println("Yig'indi: $s")
    }
}
Natija
Yig'indi: 500500
sikl: true

inline - kompilyatorga "bu funksiyaning tanasini chaqirilgan joyga ko'chirib qo'y" deydi. Shunda lambda uchun obyekt yaratilmaydi.

Standart kutubxonadagi filter, map, let - hammasi inline. Shuning uchun ular sikl yozgan bilan bir xil tezlikda ishlaydi.

Amaliy topshiriq
  1. Ikkita sonni ko'paytiradigan lambda yozing.
  2. (Int, Int) -> Int turini aniq yozing.
  3. Amalni parametr sifatida oladigan funksiya yarating.
  4. Lambda ni qavsdan tashqariga chiqarib yozing.
  5. Funksiya qaytaradigan funksiya yozing.
  6. Yopilma bilan sanagich yasang.
  7. ::nom havolasini filter da ishlating.
  8. apply bilan obyekt yaratib sozlang.
  9. also bilan zanjirdagi oraliq natijalarni chiqaring.
  10. let, run, apply farqini bitta obyektda ko'rsating.

Xulosa #

  • Lambda: { parametr -> tana }; oxirgi ifoda qaytariladi.
  • Funksiya turi: (Int, Int) -> Int.
  • Oxirgi parametr lambda bo'lsa, uni qavsdan tashqarida yozish mumkin - o'z funksiyangizni ham shunday loyihalang.
  • Yopilma: lambda tashqi o'zgaruvchini ushlab qoladi va Kotlin da var ni ham o'zgartira oladi.
  • ::nom va Tur::metod - funksiya havolalari.
  • apply va also obyektni qaytaradi; let, run, with blok natijasini.
  • let - null uchun, apply - sozlash uchun, also - kuzatish uchun.
  • inline lambda uchun obyekt yaratilishining oldini oladi.

Keyingi bo'limda generiklarni o'rganamiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.