JavaRush /Курси /Kotlin SELF /Оголошення та виклик функцій

Оголошення та виклик функцій

Kotlin SELF
Рівень 8 , Лекція 1
Відкрита

1. Оголошення функцій

Коли ви тільки починаєте програмувати, здається, що найпростіше — написати весь код у main, зверху вниз. На перших кроках це справді нормально: мозок зайнятий синтаксисом, а не архітектурою.

Але доволі швидко зʼявляються два симптоми. По-перше, ви копіюєте однакові шматки коду (а потім виправляєте баги в пʼяти місцях). По-друге, main перетворюється на простирадло, яке страшно чіпати. Функції — це спосіб дати імʼя повторюваній дії або обчисленню, щоб код читався як сценарій: «спочатку запитали введення, потім порахували, потім вивели».

Уявіть, що програма — це рецепт. main — це «що робимо крок за кроком», а функції — «як саме нарізати цибулю» і «як зварити макарони».

Мінімальна функція: fun імʼя() { ... }

Почнімо з найпростішого виду функції: без параметрів і без результату. У Kotlin функцію створюють за допомогою ключового слова fun. Далі йдуть імʼя та круглі дужки з параметрами (поки що порожні), а потім — тіло у фігурних дужках.

Важливо: функції (як і main) зазвичай оголошують на рівні файлу, тобто не «всередині main». Локальні функції розберемо пізніше. А зараз дотримуймося класики: функції — окремо, main — окремо.

fun printWelcome() {
    println("Ласкаво просимо до програми!") // Ласкаво просимо до програми!
}

fun main() {
    printWelcome()
    println("Зараз почнемо працювати...")      // Зараз почнемо працювати...
}

Тут printWelcome() — це просто «команда». Ми викликали її — і вона виконалася. Зверніть увагу на дві речі.

По-перше, оголошення функції саме по собі нічого не запускає. Поки ви не викликали printWelcome() із main, програма взагалі не зобовʼязана згадувати, що така функція існує.

По-друге, імʼя функції — не місце для фантазій. Хороші імена починаються з дії: printWelcome, readAge, calculateTotal. З ними код починає читатися майже як звичайний текст.

Параметри й аргументи

Майже одразу хочеться зробити функцію такою, щоб вона могла працювати з різними даними. Для цього й існують параметри.

Параметри — це змінні, які функція отримує на вхід. Їх оголошують у круглих дужках, і вони обовʼязково мають мати тип. Аргументи — це конкретні значення, які ви передаєте під час виклику.

Спробуймо звести ці факти в невелику таблицю:

Термін Де живе Приклад Як розуміти
Параметр в оголошенні функції
name: String
«що функція очікує на вхід»
Аргумент у виклику функції
"Kotlin"
«що насправді передали»

Тепер приклад. Зробімо функцію, яка друкує привітання для конкретного користувача:

fun greet(name: String) {
    println("Привіт, $name!") // Привіт, Kotlin!
}

fun main() {
    greet("Kotlin")
    greet("світ")
}

Тут name: String — параметр, а "Kotlin" і "світ" — аргументи. Зверніть увагу: параметр name доступний лише всередині тіла функції. Функція завершилася — параметр зник.

Функції з кількома параметрами

Коли параметрів кілька, їх перелічують через кому. І тут зʼявляються перші «людські» помилки: переплутати порядок аргументів легше, ніж здається (особливо якщо тип однаковий, наприклад два Int).

Зробімо функцію, яка друкує «рамку» для заголовка. Наче дрібниця, але такі дрібниці чудово тренують мозок: ви одразу бачите користь від принципу «дати імʼя повторюваному шматку коду».

fun printTitle(title: String, width: Int) {
    println("=".repeat(width))
    println(title)
    println("=".repeat(width))
}

fun main() {
    printTitle("Звіт", 10)
}

Якби ви випадково викликали printTitle(10, "Звіт"), компілятор не дав би скомпілювати програму, бо типи не збігаються (і це добре). А от якби було printTitle(width: Int, height: Int) — компілятор би пропустив, а логіка зламалася б. Тож промовисті імена параметрів — це не лише естетика, а й захист від помилок.

2. Значення, що повертаються, і сигнатури

Тип, що повертається: функція як «вираз», а не просто «дія»

До цього ми робили функції, які щось друкують. Але дуже часто функція має порахувати й віддати результат назад. Тоді кажуть, що функція «повертає значення».

У Kotlin тип, що повертається, пишуть після списку параметрів через двокрапку: : Int, : String, : Boolean тощо. Приклад із базового синтаксису Kotlin — функція sum(a: Int, b: Int): Int, яка повертає суму.

fun sum(a: Int, b: Int): Int {
    return a + b
}

fun main() {
    val total = sum(10, 20)
    println(total) // 30
}

Не лякайтеся слова return: у наступній лекції розберемо, як саме функція «завершується» і що означає «повернути значення». А поки зафіксуймо головне: якщо у функції вказано тип, що повертається, : Int, то результат виклику можна присвоїти змінній і використовувати далі.

Важливе спостереження: виклик функції, що повертає значення, — це вираз. Тобто його можна підставити всередину рядка або іншого обчислення.

fun sum(a: Int, b: Int): Int {
    return a + b
}

fun main() {
    println("2 + 3 = ${sum(2, 3)}") // 2 + 3 = 5
}

Ми буквально вставили результат функції в шаблон рядка. Ви вже вмієте робити це зі змінними; тепер учимося робити так само й із функціями.

Сигнатура функції як контракт

Коли ви бачите функцію, корисно сприймати її заголовок як контракт. Це дуже допомагає, коли код стає більшим і ви вже не памʼятаєте, що де написано.

Візьмімо приклад:

fun average(total: Int, days: Int): Double {
    return total.toDouble() / days
}

Її сигнатура — це:

  • імʼя: average
  • параметри: (total: Int, days: Int)
  • тип, що повертається: Double

І це вже майже все, що вам потрібно, щоб користуватися функцією. Навіть не читаючи тіло, можна зрозуміти: «дайте мені total і days, я поверну Double».

Це можна уявити як невелику схему:

flowchart LR
    A["Аргументи у виклику<br/>average(7000, 7)"] --> B["Параметри функції<br/>total: Int, days: Int"]
    B --> C["Тіло функції<br/>обчислення"]
    C --> D["Результат виклику<br/>Double"]

Якщо ви вмієте читати сигнатури, ви починаєте програмувати швидше: менше стрибаєте очима по файлах, менше покладаєтеся на інтуїцію, а більше — на контракт.

Параметри всередині функції — лише для читання

У новачків часто виникає бажання зробити так: «я отримав параметр, зараз його підправлю і поверну». Але Kotlin не дає переназначати параметри. Це зроблено навмисно: параметри — це вхідні дані, а не робочі змінні. Сприймайте їх як val-змінні. Адже саме так їх і трактує компілятор Kotlin.

Тобто так робити не можна (компілятор насвариться):

fun makeLouder(text: String): String {
    text = text.uppercase() // Помилка компіляції: Val cannot be reassigned
    return text.uppercase()
}

Якщо вам потрібно щось змінювати, створіть локальну змінну:

fun normalizeName(name: String): String {
    var result = name.trim()
    result = result.replaceFirstChar { it.uppercase() }
    return result
}

fun main() {
    println(normalizeName("  kotlin  ")) // Kotlin
}

Тут name — вхідні дані, а result — робоча зона.

3. Міні-застосунок «Трекер кроків»

Зараз ми зберемо невеликий, але цілісний приклад, який допоможе вам поєднати все вивчене. Нехай це буде консольний «трекер кроків за тиждень»: програма запитує кроки за кожен день, зберігає їх у масиві й друкує підсумок.

Раніше ви б написали все в main. Тепер же почнемо виносити повторювані кроки у функції. Ми свідомо робимо код простим, щоб фокус був на функціях, а не на хитрих прийомах.

Функція-помічник для друку заголовка

Коли програма зростає, приємно мати єдиний стиль виводу.

fun printSection(title: String) {
    println()
    println("=== $title ===")
}

fun main() {
    printSection("Трекер кроків")
}

Функція, яка читає число

Валідацію введення ми робитимемо пізніше. Але вже зараз можна винести «попросити й прочитати» в окрему функцію.

fun readInt(prompt: String): Int {
    print(prompt)
    val raw = readln().trim()
    val number = raw.toIntOrNull()

    if (number == null) return 0
    return number
}

fun main() {
    val x = readInt("Введіть число: ")
    println("Ви ввели: $x") // наприклад: Ви ввели: 12
}

Як працює return, ми розберемо трохи пізніше. А зараз сприймайте це так: «функція зобовʼязана віддати Int, отже наприкінці повертає число».

Функція читання кроків за день

Тепер обгорнемо «введення кроків за конкретний день» у функцію з параметром dayName.

fun readStepsForDay(dayName: String): Int {
    return readInt("Кроки за $dayName: ")
}

fun main() {
    val mondaySteps = readStepsForDay("Понеділок")
    println("Понеділок: $mondaySteps") // наприклад: Понеділок: 5400
}

Зауважте, як код починає читатися по-людськи: readStepsForDay("Понеділок") — майже речення.

Функція підрахунку суми за масивом

У нас є масив IntArray. Ми вже вміємо проходити його циклом і накопичувати суму. Тепер дамо цій дії імʼя.

fun sumSteps(steps: IntArray): Int {
    var total = 0
    for (x in steps) {
        total += x
    }
    return total
}

fun main() {
    val data = intArrayOf(3000, 5000, 4500)
    println(sumSteps(data)) // 12500
}

Ця функція корисна не лише в трекері кроків. Вона також показує, як функція може «запакувати» довгий цикл і повернути підсумок.

Збираємо все в один сценарій main

Зберемо мінімальний робочий «трекер тижня» з маленьких функцій. Цей шматок буде довшим, ніж зазвичай, — це нормально. За задумом main і має бути сценарієм, який читає людина.

fun printSection(title: String) {
    println()
    println("=== $title ===")
}

fun readInt(prompt: String): Int {
    print(prompt)
    val raw = readln().trim()
    val number = raw.toIntOrNull()
    if (number == null) return 0
    return number
}

fun readStepsForDay(dayName: String): Int {
    return readInt("Кроки за $dayName: ")
}

fun sumSteps(steps: IntArray): Int {
    var total = 0
    for (x in steps) total += x
    return total
}

fun main() {
    printSection("Трекер кроків за тиждень")

    val days = arrayOf("Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Нд")
    val steps = IntArray(7)

    for (i in days.indices) {
        steps[i] = readStepsForDay(days[i])
    }

    val total = sumSteps(steps)
    println("Усього кроків: $total") // наприклад: Усього кроків: 42000
}

Що тут важливо й цікаво:

  • У кожної функції є зрозуміле імʼя та невеликий обсяг.
  • Параметри показують «що потрібно для роботи»: dayName, prompt, steps.
  • Тип, що повертається, показує «що функція віддає»: Int.
  • main перестав бути звалищем деталей і перетворився на сценарій: «показати заголовок → зібрати дані → порахувати → вивести».

4. Типові помилки під час оголошення та виклику функцій

Помилка № 1: забули вказати тип параметра.
Новачків часто тягне написати fun greet(name) { ... }, як у деяких мовах. Kotlin так не вміє: тип параметра обовʼязковий. Правильно: fun greet(name: String) { ... }. Спочатку це дратує, а потім рятує від дуже дивних багів, коли «не те передали й не туди».

Помилка № 2: переплутали параметри й аргументи (і через це плутаєтеся в поясненнях).
Звучить як термінологія заради термінології, але на практиці це допомагає. Параметри — це те, що оголошено у функції. Аргументи — те, що ви реально передали під час виклику. Якщо ви називаєте все «параметрами», у якийсь момент ви й самі не зможете пояснити, що саме не збіглося.

Помилка № 3: намагаєтеся змінити параметр усередині функції.
Після досвіду з var хочеться «підкрутити вхідне значення» прямо в параметрі. Kotlin не дозволяє переназначати параметри: вони поводяться як val. Якщо потрібно щось змінювати — створіть локальну var і змінюйте її. Код стане трохи довшим, зате в рази зрозумілішим.

Помилка № 4: функція робить занадто багато всього одразу.
Коли ви тільки починаєте, здається зручним написати fun doAll() — «і введення, і підрахунок, і друк». Але тоді функцію неможливо перевикористати: ви хотіли «тільки порахувати», а вона ще й друкує; хотіли «тільки запитати», а вона вже лізе в масив. Хороше правило на старті: одна функція — один зрозумілий крок сценарію або одне обчислення.

Помилка № 5: імена рівня f1, calc, doIt, бо «і так зрозуміло».
Поки застосунок має 20 рядків — так, «і так зрозуміло». Коли стане 200 — уже ні. Імʼя функції має відповідати на запитання «що вона робить?» або «що вона повертає?». sumSteps читається без коментарів, а calc2 — як загадка з квесту.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ