JavaRush /Курси /Kotlin SELF /for: діапазони a..b і перебір символів рядка

for: діапазони a..b і перебір символів рядка

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

1. Навіщо потрібен for, якщо вже є while?

Якщо чесно, на while можна написати майже все… Але іноді це виглядає як спроба забити цвях мікроскопом: технічно можливо, та дуже незграбно. for потрібен для ситуацій, коли повторення йде за заздалегідь підготовленим «джерелом значень»: діапазоном чисел, рядком, масивом або колекцією. У Kotlin for саме так і задуманий: «перебирай те, що вміє віддавати елементи по черзі».

У побутовій аналогії while — це «поки в холодильнику є їжа, продовжуй їсти», а for — «зʼїжте 5 цукерок» або «переберіть усі літери в слові». В обох випадках ви повторюєте дії, але в другому простіше описати повторення без ручного керування лічильником.

2. Синтаксис for: «для кожного … у …»

for читається майже як українська фраза: «для кожного елемента x із джерела значень зроби тіло». Загальна форма така: for (item in source) { ... }. Це не магія і не заклинання — просто дуже зручний запис, який приховує рутину: взяти чергове значення, виконати тіло, перейти до наступного.

Важливо одразу звикнути до думки: змінна циклу (item, i, ch) — це «поточне значення», а не «змінна-лічильник, яку ми зобовʼязані вручну збільшувати». У for крок змінює сам цикл (за вас), і це одна з причин, чому код виходить коротшим і спокійнішим.

Найпростіший приклад — пройтися списком слів (поки ми не вивчаємо колекції глибоко, це лише демонстрація ідеї):

fun main() {
    val shoppingList = listOf("Milk", "Bananas", "Bread")

    for (item in shoppingList) {
        println(item)
    }
    // Milk
    // Bananas
    // Bread
}

3. Діапазони a..b у for

Діапазон a..b — це спосіб сказати: «візьми всі числа від a до b включно». І тут ключове слово — включно. Якщо ви пишете 1..5, то будуть значення 1, 2, 3, 4 і 5 — усього пʼять кроків. Саме такий діапазон найчастіше використовують, коли потрібно повторити дію N разів або пройти «від і до».

У документації Kotlin for описується як цикл, який уміє перебирати діапазони (ranges) поряд із масивами та колекціями. Це означає, що for (i in 1..10) — абсолютно «рідний» стиль Kotlin, а не випадкова фішка.

Мініприклад: «зроби 5 кроків»

Зробімо максимально прозорий приклад — просто надрукуємо номер кроку:

fun main() {
    for (i in 1..5) {
        println("Крок $i")
    }
    // Крок 1
    // Крок 2
    // Крок 3
    // Крок 4
    // Крок 5
}

Тут важливо помітити дві речі.

По‑перше, ми не пишемо i = i + 1. Змінну i змінює цикл, а ми лише використовуємо поточний i.

По‑друге, змінна i всередині циклу поводиться як val: вона створюється заново для кожної ітерації як «поточне значення». Якщо спробувати зробити i = i + 1, компілятор буде незадоволений. І це, загалом, на краще: менше способів зламати логіку.

Табличка про «включно»

Щоб менше наступати на граблі, корисно один раз зафіксувати:

Діапазон Значення всередині Скільки ітерацій
1..1
1
1
1..3
1, 2, 3
3
2..5
2, 3, 4, 5
4

Якщо вам потрібно «рівно N повторів, починаючи з 1», майже завжди підходить 1..N.

for як «повторити N разів» без ручного лічильника

У попередніх лекціях із while ми робили класичний «лічильник»: оголосити var i = 1, перевірити умову, а всередині збільшити i. Це нормально й інколи необхідно. Але коли крок очевидний і лінійний (1, 2, 3…), for робить код компактнішим і читабельнішим: ви буквально бачите межі прямо в заголовку циклу.

Із практичного погляду це означає: якщо задача звучить як «зроби 10 спроб», «прочитай 3 значення», «порахуй суму від 1 до n», то for зазвичай буде вашим першим кандидатом. Він знижує ризик нескінченного циклу, тому що ви не забудете збільшити лічильник — його просто немає.

Підсумовування: сума від 1 до n

Сума чисел від 1 до n — навчальний приклад не тому, що нам життєво важливо складати числа (хоча бухгалтерія могла б не погодитися), а тому що він тренує одразу три навички: змінну-накопичувач, діапазон і акуратну роботу з межами.

Зробімо мініпрограму: попросимо n і, якщо воно коректне та n >= 1, порахуємо суму через for.

fun main() {
    print("Введіть n (>= 1): ")
    val n = readln().trim().toIntOrNull()

    if (n != null && n >= 1) {
        var sum = 0
        for (x in 1..n) sum += x
        println("sum = $sum")
    } else {
        println("Некоректне n")
    }
}

Тут є маленька хитрість для читабельності: for (x in 1..n) sum += x можна писати в один рядок, тому що тіло циклу — одна команда. Kotlin це дозволяє. Але якщо у вас у циклі дві-три дії, краще ставити { }, щоб не перетворювати програму на «поему без перенесень рядків».

Як for (x in 1..n) «думає»

Іноді новачкові простіше зрозуміти цикл, якщо уявити його як механізм, що повторює кроки. Спрощена логіка діапазонного for виглядає так (це не код Kotlin, а «як це працює за змістом»):

flowchart TD
    A[Старт циклу] --> B[Взяти наступне значення x з діапазону]
    B --> C{Є значення?}
    C -- ні --> D[Вихід із циклу]
    C -- так --> E[Виконати тіло циклу]
    E --> B

Це добре поєднується з ідеєю з документації Kotlin: for перебирає те, що віддає елементи через ітератор, а діапазони компілюються в оптимізований перебір.

4. downTo і step: напрям і крок

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

Почнемо з простого: зворотний відлік.

fun main() {
    for (i in 5 downTo 1) {
        print("$i ")
    }
    // 5 4 3 2 1
}

Під час використання downTo обидва значення входять у діапазон.

Тепер додамо крок: «через 2».

fun main() {
    for (i in 10 downTo 0 step 2) {
        print("$i ")
    }
    // 10 8 6 4 2 0
}

Тут зручно читати зліва праворуч: «Почни з 10, іди вниз до 0, крок 2».

5. Перебір рядка в for

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

У Kotlin рядок можна перебирати в for безпосередньо: for (ch in text) { ... }. На кожній ітерації ch матиме тип Char, тобто один символ. Навіть якщо ви поки не проходили окрему лекцію про Char, тут достатньо розуміти: це «одна літера/цифра/знак».

І важливо: for рядком — це все той самий принцип «перебираємо джерело значень», просто джерелом стає рядок. У документації Kotlin підкреслюється, що for працює з будь-яким обʼєктом, у якого є ітератор. Рядок якраз такий обʼєкт.

Вивести всі символи по одному

fun main() {
    val word = "Kotlin"

    for (ch in word) {
        println(ch)
    }
    // K
    // o
    // t
    // l
    // i
    // n
}

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

Підрахунки: пробіли та цифри

Зараз ми зробимо маленький «Text Analyzer v0» (версія нуль-нуль-нуль-дуже-рання): програма читає рядок і рахує, скільки в ньому пробілів і скільки цифр. Ми не будемо використовувати жодні складні штуки — лише for, if і пару лічильників.

Щоб визначити, «цифра це чи ні», можна порівняти символ із межами '0' і '9'. Символи можна порівнювати, тому що вони мають порядок (за кодами). Для новачка це звучить трохи дивно, але на практиці чудово працює для діапазонів цифр і латинських літер.

fun main() {
    print("Введіть рядок: ")
    val text = readln()

    var spaces = 0
    var digits = 0

    for (ch in text) {
        if (ch == ' ') spaces += 1
        if (ch >= '0' && ch <= '9') digits += 1
    }

    println("Пробілів: $spaces, цифр: $digits")
}

Якщо користувач введе Kotlin 2.3, виведення буде приблизно таким:

// Введіть рядок: Kotlin 2.3
// Пробілів: 1, цифр: 2

Так ми поступово підходимо до корисної звички: for часто йде разом із «лічильниками» (var), які ви оновлюєте за умовою.

6. Як вибрати: while, do-while чи for

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

while і do-while корисні, коли кількість повторів заздалегідь невідома: «повторюй, поки введення неправильне» або «роби спроби, поки не вгадаєш». А for — коли ви можете заздалегідь описати джерело кроків: діапазон чисел або послідовність символів. У документації Kotlin це формулюється прямо: for перебирає колекції, масиви, діапазони та взагалі все, що надає ітератор.

Табличка для швидкої орієнтації:

Ситуація Зазвичай краще читається
Повторювати, поки умова істинна
while
Дія має виконатися мінімум один раз
do-while
Пройти по числах від a до b
for (i in a..b)
Пройти по всіх символах рядка
for (ch in text)

7. Типові помилки під час роботи з for

Помилка №1: очікувати, що діапазон a..b «не включає b».
Це, мабуть, найчастіша плутанина після переходу з деяких інших мов — і просто через неуважність. У нашому курсі фіксуємо чітко: .. включає обидві межі. Якщо ви хочете надрукувати числа 1, 2, 3, 4, 5 — пишіть 1..5, і все буде чесно.

Помилка №2: намагатися вручну змінювати змінну циклу всередині for.
У for змінна циклу — це «поточне значення», а не ваша особиста іграшка. Якщо спробувати зробити i = i + 1, компілятор не дозволить. І це добре: так ви не зламаєте крок і не зробите цикл непередбачуваним. Якщо вам потрібно керувати кроком вручну — це вже кандидат на while (але сьогодні ми просто запамʼятовуємо принцип).

Помилка №3: використовувати for для повторного введення «поки не введуть правильно».
Іноді хочеться написати «10 разів запитаємо введення — і раптом пощастить». Але якщо користувач вводить неправильно, ви або завершите спроби й отримаєте некоректний стан, або ускладните логіку. Для «повторюй до коректного значення» природніші while або do-while, які ми вже вивчили.

Помилка №4: змішувати різні значення в одній змінній-лічильнику.
Наприклад, коли ви одночасно рахуєте суму, кількість цифр і кількість пробілів, легко випадково оновити «не той» лічильник. Це не проблема for, це проблема читабельності. Рятує просте правило: одна змінна — один сенс. digits рахує цифри, spaces — пробіли, sum — суму. І не треба робити з x універсального солдата.

Помилка №5: забувати про порожні рядки та «нічого не відбувається».
Якщо користувач увів порожній рядок, for (ch in text) просто не виконається жодного разу — і це нормальна поведінка (як і у while, коли умова одразу false). Головне — не панікувати: програма не зламалася, вона чесно не знайшла жодного символу для обробки.

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