20-bo‘lim

Amaliy loyiha - vazifalar daftari

O'rganilgan hamma narsani birlashtirib, vazifalarni yozadigan, filtrlaydigan, hisobot beradigan va faylga saqlaydigan to'liq dastur yozamiz.

🕑 11 daqiqa o‘qish 📄 739 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. 1-bosqich: model
  2. 2-bosqich: daftar
  3. 3-bosqich: hisobot
  4. 4-bosqich: faylga saqlash
  5. 5-bosqich: natijani ifodalash
  6. Interaktiv menyu
  7. Dastur tuzilishi
  8. Qaysi bo'lim qayerda ishlatildi
  9. Xulosa
  10. Darslik yakuni

Endi barcha bo'limlarni birlashtiramiz. Yozadigan dasturimiz - vazifalar daftari: vazifa qo'shadi, bajarilganini belgilaydi, hisobot beradi va faylga saqlaydi.

Dasturni bosqichma-bosqich quramiz va har bir bosqichni ishga tushirib ko'ramiz.

1-bosqich: model #

Avval ma'lumot shaklini belgilaymiz. Bu ta'riflar butun bob davomida ishlatiladi:

KOTLIN
enum class Muhimlik(val belgi: String, val vazn: Int) {
    PAST("-", 1),
    ORTA("=", 2),
    YUQORI("!", 3);

    override fun toString() = name.lowercase()
}

data class Vazifa(
    val id: Int,
    val matn: String,
    val muhimlik: Muhimlik = Muhimlik.ORTA,
    val bajarildi: Boolean = false
) {
    val korinishi: String
        get() {
            val belgi = if (bajarildi) "x" else " "
            return "[$belgi] ${muhimlik.belgi} #$id $matn"
        }
}

Sinab ko'ramiz:

KOTLIN
fun main() {
    val a = Vazifa(1, "Kotlin o'rganish", Muhimlik.YUQORI)
    val b = Vazifa(2, "Non olish")

    println(a.korinishi)
    println(b.korinishi)
    println(a)

    val bajarilgan = a.copy(bajarildi = true)
    println(bajarilgan.korinishi)
    println(a.bajarildi)
}
Natija
[ ] ! #1 Kotlin o'rganish
[ ] = #2 Non olish
Vazifa(id=1, matn=Kotlin o'rganish, muhimlik=yuqori, bajarildi=false)
[x] ! #1 Kotlin o'rganish
false

Diqqat qiling:

  • data class toString ni o'zi yozdi.
  • copy(bajarildi = true) yangi obyekt yasadi - a o'zgarmadi.
  • enum ga xossa (belgi, vazn) va toString qo'shildi.
  • korinishi - hisoblanadigan xossa, saqlanmaydi.

2-bosqich: daftar #

KOTLIN
class Daftar {
    private val vazifalar = mutableListOf<Vazifa>()
    private var keyingiId = 1

    val soni: Int get() = vazifalar.size

    fun qosh(matn: String, muhimlik: Muhimlik = Muhimlik.ORTA): Vazifa {
        require(matn.isNotBlank()) { "Vazifa matni bo'sh bo'lmasligi kerak" }
        val yangi = Vazifa(keyingiId++, matn.trim(), muhimlik)
        vazifalar.add(yangi)
        return yangi
    }

    fun bajar(id: Int): Boolean {
        val i = vazifalar.indexOfFirst { it.id == id }
        if (i == -1) return false
        vazifalar[i] = vazifalar[i].copy(bajarildi = true)
        return true
    }

    fun ochir(id: Int): Boolean = vazifalar.removeIf { it.id == id }

    fun hammasi(): List<Vazifa> = vazifalar.toList()

    fun kutilayotgan(): List<Vazifa> =
        vazifalar.filter { !it.bajarildi }
                 .sortedWith(compareByDescending<Vazifa> { it.muhimlik.vazn }
                     .thenBy { it.id })

    fun topish(soz: String): List<Vazifa> =
        vazifalar.filter { it.matn.contains(soz, ignoreCase = true) }
}
KOTLIN
fun main() {
    val d = Daftar()

    d.qosh("Kotlin o'rganish", Muhimlik.YUQORI)
    d.qosh("Non olish", Muhimlik.PAST)
    d.qosh("Hisobot yozish", Muhimlik.YUQORI)
    d.qosh("Kitob o'qish")

    println("Jami: ${d.soni}")

    d.bajar(2)

    println("-- Kutilayotganlar --")
    d.kutilayotgan().forEach { println(it.korinishi) }

    println("-- Qidiruv: 'o' --")
    d.topish("o").forEach { println(it.korinishi) }

    println(d.bajar(99))
}
Natija
Jami: 4
-- Kutilayotganlar --
[ ] ! #1 Kotlin o'rganish
[ ] ! #3 Hisobot yozish
[ ] = #4 Kitob o'qish
-- Qidiruv: 'o' --
[ ] ! #1 Kotlin o'rganish
[x] - #2 Non olish
[ ] ! #3 Hisobot yozish
[ ] = #4 Kitob o'qish
false

Oxirgi false - d.bajar(99) natijasi: bunday id yo'q, shuning uchun metod istisno ko'tarmasdan false qaytardi.

Bu yerda ishlatilgan vositalar:

  • require - bo'sh matnni rad etadi (17-bo'lim).
  • indexOfFirst va copy - o'zgarmas obyektni "yangilash".
  • compareByDescending { }.thenBy { } - ikki mezon bo'yicha saralash.
  • contains(soz, ignoreCase = true) - katta-kichik harfsiz qidiruv.
  • hammasi() nusxa qaytaradi - toList() shuning uchun.
Ichki ro'yxatni to'g'ridan-to'g'ri bermang

hammasi() da vazifalar ni to'g'ridan-to'g'ri qaytarsa bo'lardi - turi List bo'lgani uchun tashqaridan add chaqirib bo'lmasdi.

Lekin uni MutableList ga aylantirib olish mumkin:

KOTLIN
(d.hammasi() as MutableList).clear()

toList() esa haqiqiy nusxa yasaydi - bunday hiyla ishlamaydi.

3-bosqich: hisobot #

KOTLIN
fun hisobot(d: Daftar): String {
    val hammasi = d.hammasi()
    if (hammasi.isEmpty()) return "Daftar bo'sh"

    val bajarilgan = hammasi.count { it.bajarildi }
    val foiz = bajarilgan * 100.0 / hammasi.size

    val quruvchi = StringBuilder()
    quruvchi.appendLine("MUHIMLIK    JAMI  BAJARILDI")
    quruvchi.appendLine("-".repeat(28))

    Muhimlik.entries.sortedByDescending { it.vazn }.forEach { m ->
        val guruh = hammasi.filter { it.muhimlik == m }
        if (guruh.isNotEmpty()) {
            quruvchi.appendLine(
                m.name.padEnd(12) +
                guruh.size.toString().padStart(4) +
                guruh.count { it.bajarildi }.toString().padStart(11)
            )
        }
    }

    quruvchi.appendLine("-".repeat(28))
    val f = "%.0f".format(foiz)
    quruvchi.append("Bajarilgan: $bajarilgan/${hammasi.size} ($f%)")
    return quruvchi.toString()
}

fun main() {
    val d = Daftar()
    d.qosh("Kotlin o'rganish", Muhimlik.YUQORI)
    d.qosh("Non olish", Muhimlik.PAST)
    d.qosh("Hisobot yozish", Muhimlik.YUQORI)
    d.qosh("Kitob o'qish")
    d.qosh("Sport", Muhimlik.PAST)

    d.bajar(2)
    d.bajar(3)

    println(hisobot(d))
    println()
    println(hisobot(Daftar()))
}
Natija
MUHIMLIK    JAMI  BAJARILDI
----------------------------
YUQORI         2          1
ORTA           1          0
PAST           2          1
----------------------------
Bajarilgan: 2/5 (40%)

Daftar bo'sh

Muhimlik.entries - enum ning hamma qiymati (12-bo'lim). Uni vazn bo'yicha saralab, tartibni o'zimiz belgiladik.

4-bosqich: faylga saqlash #

KOTLIN
import java.io.File

fun Vazifa.qatorga(): String =
    listOf(id, matn, muhimlik.name, bajarildi).joinToString("|")

fun qatordan(qator: String): Vazifa? {
    val q = qator.split("|")
    if (q.size != 4) return null
    val id = q[0].toIntOrNull() ?: return null
    val muhimlik = runCatching { Muhimlik.valueOf(q[2]) }.getOrNull()
        ?: return null
    return Vazifa(id, q[1], muhimlik, q[3].toBoolean())
}

fun saqla(d: Daftar, fayl: File) {
    fayl.writeText(d.hammasi().joinToString("\n") { it.qatorga() })
}

fun yukla(fayl: File): List<Vazifa> {
    if (!fayl.exists()) return emptyList()
    return fayl.readLines()
        .filter { it.isNotBlank() }
        .mapNotNull { qatordan(it) }
}

fun main() {
    val fayl = File("vazifalar.txt")

    val d = Daftar()
    d.qosh("Kotlin o'rganish", Muhimlik.YUQORI)
    d.qosh("Non olish", Muhimlik.PAST)
    d.bajar(2)

    saqla(d, fayl)
    println("Saqlandi:")
    println(fayl.readText())

    println("-- Yuklandi --")
    yukla(fayl).forEach { println(it.korinishi) }

    fayl.writeText("bu|buzuq\nyana|buzuq|qator|va|ortiqcha")
    println("Buzuq fayldan: ${yukla(fayl).size} ta")

    println("Yo'q fayldan: ${yukla(File("yoq.txt")).size} ta")

    fayl.delete()
}
Natija
Saqlandi:
1|Kotlin o'rganish|YUQORI|false
2|Non olish|PAST|true
-- Yuklandi --
[ ] ! #1 Kotlin o'rganish
[x] - #2 Non olish
Buzuq fayldan: 0 ta
Yo'q fayldan: 0 ta

Diqqat: buzuq fayl ham, mavjud bo'lmagan fayl ham dasturni to'xtatmadi. Yaxshi dasturning belgisi shu.

Bu yerda Vazifa.qatorga() - kengaytma funksiyasi (14-bo'lim). U Vazifa sinfiga tegmasdan unga saqlash qobiliyatini qo'shdi.

5-bosqich: natijani ifodalash #

Foydalanuvchi buyrug'ining natijasini sealed bilan ifodalaymiz (13-bo'lim):

KOTLIN
sealed interface Javob {
    data class Muvaffaqiyat(val xabar: String) : Javob
    data class Xato(val sabab: String) : Javob
    data object Chiqish : Javob
}

fun buyruq(d: Daftar, satr: String): Javob {
    val qismlar = satr.trim().split(" ", limit = 2)
    val amal = qismlar.firstOrNull()?.lowercase()
        ?: return Javob.Xato("bo'sh")
    val arg = qismlar.getOrNull(1)?.trim()

    return when (amal) {
        "qosh" -> {
            if (arg.isNullOrBlank()) return Javob.Xato("matn kerak")
            val v = d.qosh(arg)
            Javob.Muvaffaqiyat("Qo'shildi: #${v.id}")
        }
        "bajar" -> {
            val id = arg?.toIntOrNull() ?: return Javob.Xato("id kerak")
            if (d.bajar(id)) Javob.Muvaffaqiyat("Bajarildi: #$id")
            else Javob.Xato("#$id topilmadi")
        }
        "ochir" -> {
            val id = arg?.toIntOrNull() ?: return Javob.Xato("id kerak")
            if (d.ochir(id)) Javob.Muvaffaqiyat("O'chirildi: #$id")
            else Javob.Xato("#$id topilmadi")
        }
        "chiqish" -> Javob.Chiqish
        else -> Javob.Xato("noma'lum buyruq: $amal")
    }
}

fun main() {
    val d = Daftar()

    val buyruqlar = listOf(
        "qosh Kotlin o'rganish",
        "qosh Non olish",
        "bajar 1",
        "bajar 99",
        "ochir abc",
        "qosh",
        "sakrash",
        "chiqish"
    )

    for (b in buyruqlar) {
        val javob = buyruq(d, b)
        val belgi = when (javob) {
            is Javob.Muvaffaqiyat -> "OK   ${javob.xabar}"
            is Javob.Xato -> "XATO ${javob.sabab}"
            Javob.Chiqish -> "-- chiqildi --"
        }
        println("$belgi")
        if (javob is Javob.Chiqish) break
    }

    println("Qolgan: ${d.soni} ta")
}
Natija
OK   Qo'shildi: #1
OK   Qo'shildi: #2
OK   Bajarildi: #1
XATO #99 topilmadi
XATO id kerak
XATO matn kerak
XATO noma'lum buyruq: sakrash
-- chiqildi --
Qolgan: 2 ta

when (javob) da else yo'q - Javob sealed bo'lgani uchun kompilyator hamma variant qamrab olinganini biladi. Yangi javob turi qo'shsangiz, u shu yerni ko'rsatib xato beradi.

Interaktiv menyu #

Haqiqiy dasturda buyruqlar klaviaturadan o'qiladi. Bu darslikda misollar avtomatik tekshirilgani uchun uni ishga tushira olmaymiz, lekin shakli quyidagicha:

KOTLIN
import java.io.File

fun main() {
    val fayl = File("vazifalar.txt")
    val d = Daftar()
    yukla(fayl).forEach { d.qosh(it.matn, it.muhimlik) }

    println("Buyruqlar: qosh <matn> | bajar <id> | ochir <id>")
    println("           royxat | chiqish")

    while (true) {
        print("> ")
        val satr = readlnOrNull() ?: break

        if (satr.trim().lowercase() == "royxat") {
            d.kutilayotgan().forEach { println(it.korinishi) }
            continue
        }

        when (val javob = buyruq(d, satr)) {
            is Javob.Muvaffaqiyat -> println(javob.xabar)
            is Javob.Xato -> println("Xato: ${javob.sabab}")
            Javob.Chiqish -> {
                saqla(d, fayl)
                println("Saqlandi. Xayr!")
                return
            }
        }
    }
}

readlnOrNull() - klaviaturadan bir qator o'qiydi va oqim tugasa null qaytaradi.

Foydalanuvchi kiritishiga hech qachon ishonmang

Yuqoridagi buyruq funksiyasida har bir qadamda tekshiruv bor: toIntOrNull, isNullOrBlank, getOrNull.

Foydalanuvchi "bajar abc" deb yozsa ham, "qosh" ni yolg'iz yozsa ham, dastur qulamaydi - u xushmuomala xabar beradi.

Bu kichik dasturda ortiqcha tuyulishi mumkin, lekin odat shu yerda shakllanadi.

Dastur tuzilishi #

Mas'uliyatlarning taqsimlanishi main (menyu) foydalanuvchi bilan gaplashadi buyruq() + Javob matnni amalga aylantiradi Daftar qoidalarni saqlaydi hisobot() kolleksiyalar saqla/yukla fayl Har bir qism bitta ish qiladi - birini o'zgartirish boshqasini buzmaydi
Kichik dasturda ham mas'uliyatni ajratish odatini shakllantiring

Qaysi bo'lim qayerda ishlatildi #

Bo'limLoyihada
3 - Turlarval, Int, Boolean
4 - SatrlarpadEnd, padStart, shablonlar
5 - whenbuyruq() funksiyasi
6 - SikllarforEach, for
7 - FunksiyalarStandart argumentlar, require
8 - Null?:, toIntOrNull, isNullOrBlank
9 - KolleksiyalarmutableListOf, toList
10 - Amallarfilter, count, sortedWith, mapNotNull
11 - SinflarDaftar, private maydon
12 - data/enumVazifa, Muhimlik, copy
13 - sealedJavob
14 - KengaytmaVazifa.qatorga()
17 - Istisnolarrequire, runCatching
18 - Fayllarsaqla, yukla
Amaliy topshiriq
  1. Loyihani o'z kompyuteringizda yig'ib, ishga tushiring.
  2. Muhimlik ga yangi qiymat qo'shing va hisobotni tekshiring.
  3. Vazifaga muddat (sana) maydonini qo'shing.
  4. Faqat bajarilganlarni qaytaruvchi metod yozing.
  5. Vazifa matnini tahrirlash imkonini qo'shing.
  6. buyruq() ga royxat amalini qo'shing.
  7. Javob ga yangi turi qo'shib, kompilyator nima deyishini ko'ring.
  8. Hisobotni faylga saqlaydigan funksiya yozing.
  9. Bajarilgan vazifalarni tozalaydigan amal qo'shing.
  10. Fayl formatini CSV dan JSON ga o'zgartirishni rejalashtiring.

Xulosa #

  • Katta dastur kichik qismlardan quriladi: model, qoidalar, hisobot, saqlash - har biri alohida.
  • data class + copy - o'zgarmas ma'lumot bilan ishlashning asosi.
  • Ichki ro'yxatni toList() bilan nusxa qilib bering.
  • sealed natijani ifodalash uchun ideal: when to'liq bo'ladi.
  • Kengaytma funksiyasi sinfga tegmasdan unga qobiliyat qo'shadi.
  • Buzuq fayl yoki noto'g'ri kiritish dasturni to'xtatmasligi kerak.
  • Foydalanuvchi ma'lumotini har doim toIntOrNull va isNullOrBlank bilan tekshiring.

Darslik yakuni #

Tabriklaymiz - siz Kotlin ning asoslarini o'rgandingiz:

  • val/var, turlar, satrlar va when
  • Sikllar, funksiyalar va null xavfsizligi
  • Kolleksiyalar va ularning boy amallari
  • Sinflar, data class, object, enum va sealed
  • Kengaytmalar, lambda, qamrov funksiyalari va generiklar
  • Istisnolar, fayllar va korutinlar

Keyingi qadamlar uchun uchta yo'nalish bor:

  • Android - Google ning rasmiy tavsiyasi. Jetpack Compose bilan interfeys yozish bugungi standart.
  • Server - Ktor (Kotlin uchun yozilgan) yoki Spring Boot bilan veb xizmatlar.
  • Ko'p platformali - bitta mantiqni Android va iOS uchun ulashish.

Eng muhimi - yozishni to'xtatmang. Har kuni kichik dastur yozish bir oyda katta farq qiladi.

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.