1. Вступ
Коли ви вперше опанували while і for, може зʼявитися відчуття: «Ну все, тепер я можу повторювати що завгодно й скільки завгодно разів (а іноді навіть випадково — нескінченно)». Але в реальних програмах майже завжди трапляються моменти, коли повторення потрібно зупинити раніше або, навпаки, пропустити конкретний крок, бо він «поганий» або «не підходить». Саме тут і стають у пригоді break та continue: вони допомагають зробити код коротшим і чеснішим за змістом, але за необережності перетворюють цикл на мінілабіринт.
Kotlin підтримує традиційні оператори break і continue для циклів. Тобто це не «хак» і не магія, а звичайна частина мови, яку важливо розуміти так само впевнено, як if/else.
Уявіть цикл як чергу в кавʼярні: ітерації — це люди. Іноді хтось каже «я передумав» — і черга закінчується (це break). А іноді людина просто відходить «по гаманець» і пропускає обслуговування на цьому кроці, але черга триває (це continue). Важливо лише памʼятати: ці команди діють усередині найближчого циклу. Вони не «телепортують» виконання «кудись», а переводять його в чітко визначені місця.
Невелика схема того, що відбувається в циклі, якщо в нас є і continue, і break:
flowchart TD
A[Початок ітерації] --> B{Умова циклу істинна?}
B -- ні --> Z[Вихід із циклу]
B -- так --> C[Тіло циклу]
C --> D{Потрібно пропустити?}
D -- так --> E[continue]
E --> A
D -- ні --> F{Потрібно вийти?}
F -- так --> G[break]
G --> Z
F -- ні --> A
2. break: як достроково завершити цикл
break — це команда «вийти з циклу просто зараз». Вона корисна, коли подальші ітерації не мають сенсу: ви вже знайшли те, що шукали, отримали команду «вихід», зустріли «стоп-значення» або виявили стан, у якому продовжувати не можна. Головне — розуміти, що break припиняє поточний цикл і передає керування на рядок після нього. Це не «вихід із програми», а саме вихід з одного конкретного циклу.
У Kotlin break працює і в while, і в do-while, і в for. Ефект завжди однаковий: він миттєво завершує найближчий цикл. З погляду читабельності break найчастіше доречний тоді, коли точка виходу логічно зрозуміла: «введення завершено», «знайдено», «далі безглуздо». Якщо break розкиданий по коду як конфеті, мозок читача починає сумувати.
break у сценарії «введення до сигналу завершення»
У консольних програмах дуже часто введення роблять «до сигналу»: користувач вводить числа, а число 0 означає «завершити». Це як кнопка «Стоп» на мікрохвильовці: не важливо, скільки часу ви планували гріти, якщо ви вже дістали тарілку.
fun main() {
var sum = 0
while (true) {
print("Введіть число (0 — кінець): ")
val n = readln().trim().toIntOrNull()
if (n == null) {
println("Це не число, спробуйте ще раз.")
continue
}
if (n == 0) break
sum += n
}
println("Сума = $sum") // наприклад: Сума = 17
}
Тут ми використовуємо звʼязку while (true) + break як цілком нормальний прийом: умову завершення (введення 0) зручніше перевіряти усередині, бо вона залежить від того, що ввів користувач. Це читається чесно: «крутимося нескінченно, доки не скажуть зупинитися». Але нескінченний цикл доречний лише тоді, коли точка break очевидна й не захована в пʼяти рівнях умов.
break у for: «знайшли — виходимо»
Іноді for пробігає по діапазону, і нам потрібно зупинитися на першому відповідному значенні. Наприклад: знайдемо перше число від 1 до 50, яке ділиться на 7 і на 5 (тобто на 35). Можна, звісно, виписати формулу, але ми ж тренуємо цикли, а не пророчі здібності.
fun main() {
var found = -1
for (x in 1..50) {
if (x % 7 == 0 && x % 5 == 0) {
found = x
break
}
}
println("Знайшли: $found") // Знайшли: 35
}
Зверніть увагу на прийом із found = -1. Це «значення за замовчуванням», яке означає «не знайдено». Ми поки не вивчаємо null-safety як окрему тему (це буде пізніше), тому для новачків такий спосіб часто простіший: обираємо значення, яке в нормальному результаті не трапляється.
Чому break не має бути захований
Дуже поширена проблема — написати break так, що його складно помітити. Технічно код працюватиме, але читати його буде неприємно — як договір на 40 сторінок дрібним шрифтом.
Погана звичка виглядає приблизно так: усередині циклу багато розгалужень, і break сидить десь глибоко. Щоб уникнути цього, намагайтеся ставити перевірки виходу «ближче до початку ітерації», особливо якщо вони повʼязані з вхідними даними. Так мозку простіше: спочатку перевірили «ми продовжуємо?», а потім робимо корисну роботу.
3. continue: як пропустити поточну ітерацію
continue — це команда «закінчити поточну ітерацію просто зараз і перейти до наступної». Тобто цикл не завершується, але решта тіла не виконується. Якщо уявити ітерацію як мінісценарій, то continue — це «пропускаємо цю серію й переходимо до наступної».
Kotlin, як і багато мов, підтримує continue у циклах. На практиці continue особливо корисний, коли ви хочете зробити «фільтр»: деякі значення обробляємо, а деякі — пропускаємо. Це часто робить код коротшим, бо прибирає зайву вкладеність if.
continue як фільтр у for
Припустімо, ми хочемо вивести лише непарні числа від 1 до 10. Можна написати умову «якщо непарне — друкуй», а можна перевернути логіку: «якщо парне — пропусти».
fun main() {
for (i in 1..10) {
if (i % 2 == 0) continue
println(i) // 1 3 5 7 9 (кожне з нового рядка)
}
}
Чому така форма іноді зручніша? Тому що після continue ви можете писати «основну» логіку без зайвого else. Це схоже на охоронця на вході: «не підходите — проходьте повз», а всередині клубу вже нормальна вечірка (тобто основний код).
continue у while: обережно, можна зробити нескінченний цикл
У for крок відбувається автоматично, а от while тримається на вашій дисципліні: ви зобовʼязані змінювати стан так, щоб цикл колись закінчився. І саме тут continue може «вистрілити в ногу», бо він легко перестрибує важливі рядки, зокрема оновлення лічильника.
Подивіться на приклад: ми хочемо вивести числа 1..5, але пропустити 3. Якщо написати наївно, можна випадково отримати нескінченний цикл, який «зависне» на 3.
Неправильний варіант (показую як антиприклад, не робіть так):
fun main() {
var i = 1
while (i <= 5) {
if (i == 3) continue
println("i = $i")
i = i + 1
}
}
Що тут сталося? За умови i == 3 спрацьовує continue, а рядок i = i + 1 пропускається. Отже i назавжди залишається 3, і цикл ніколи не закінчиться. Це той самий момент, коли компʼютер стає надто буквальним другом: ви сказали «продовжуй», а він і продовжує… вічно.
Правильний варіант — оновлювати i до continue (або перебудувати код так, щоб оновлення відбувалося завжди):
fun main() {
var i = 1
while (i <= 5) {
if (i == 3) {
i = i + 1
continue
}
println("i = $i")
i = i + 1
}
}
Так, це виглядає трохи довше, зате логіка коректна: стан, від якого залежить умова while, змінюється завжди.
4. break і continue: разом і без болю для читача
Мінісценарій «скарбничка чисел»
Зараз зберемо невеликий цілісний сценарій, який виглядає як реальна консольна програма, а не набір окремих фрагментів. Ми зробимо «скарбничку»: користувач вводить числа (наприклад, «скільки монет поклали»), а програма накопичує суму. Введення завершиться командним числом 0. Неправильні рядки й відʼємні значення ми ігноруватимемо, але не завершуватимемо програму — просто попросимо ввести ще раз.
Тут якраз природно мати continue для «поганого введення» і break для «кінця».
fun main() {
var total = 0
while (true) {
print("Введіть суму (ціле >= 0, 0 — завершити): ")
val raw = readln().trim()
val value = raw.toIntOrNull()
if (value == null) {
println("Схоже, що це не ціле число.")
continue
}
if (value < 0) {
println("Відʼємне не приймаємо. Ми накопичуємо, а не сумуємо.")
continue
}
if (value == 0) break
total += value
println("Зараз у скарбничці: $total") // наприклад: Зараз у скарбничці: 15
}
println("Готово. Підсумок: $total") // наприклад: Готово. Підсумок: 15
}
Зверніть увагу на важливу деталь: порядок перевірок тут не випадковий. Спочатку ми зʼясовуємо, чи це взагалі число (toIntOrNull()), потім перевіряємо діапазон (чи не відʼємне), далі — «стоп-значення» (0), і лише після цього робимо корисну роботу (додаємо до total). Такий порядок робить код передбачуванішим, а отже — легшим для налагодження.
Як тримати код читабельним із break і continue
На цьому етапі навчання є одна дуже типова спокуса: «О, тепер я можу стрибати по циклу! Давайте додамо ще кілька continue, ще один break і ще один if, і вийде… еее… що вийде». З break/continue важливо памʼятати просте правило читабельності: якщо ви не можете швидко пояснити вголос, чому цикл може закінчитися і в яких випадках ітерація пропускається, значить код уже час спрощувати.
Оскільки ми поки не використовуємо користувацькі функції (це буде пізніше), спрощення зазвичай робиться трьома способами. По-перше, винесенням проміжних значень у зрозумілі змінні (raw, value, isValid). По-друге, розташуванням «перевірок-охоронців» на початку ітерації: усе, що веде до continue або break, намагаємося ставити вище. По-третє, обережним ставленням до while (true): він добрий, коли зміст «цикл до команди виходу» читається явно, але поганий, коли break захований далеко й неочевидний.
Є ще один нюанс, який стане в пригоді вже в наступній лекції про вкладені цикли: break і continue без додаткових засобів належать до найближчого циклу. Сьогодні ми глибоко в це не занурюємося, але важливо хоча б памʼятати: «вихід із двох циклів одним break» за замовчуванням не станеться — і це нормально.
5. Типові помилки під час роботи з break і continue
Помилка №1: плутати ролі break і continue.
Новачки іноді очікують, що continue «вийде з циклу», але він лише пропускає поточну ітерацію й переходить до наступної. У результаті програма починає «крутитися далі», хоча людина хотіла зупинитися. Якщо вам потрібно закінчити цикл повністю — використовуйте break. Якщо потрібно просто не виконувати решту коду ітерації — використовуйте continue.
Помилка №2: continue у while перестрибує оновлення стану.
Це класика нескінченних циклів: лічильник або інший стан оновлюється наприкінці тіла, але на деяких гілках стоїть continue, який перескакує оновлення. Цикл «зависає» на одному й тому самому значенні і не може дійти до умови завершення. Лікується дисципліною: змінна, яка бере участь в умові while, має гарантовано змінюватися так, щоб колись умова стала false — навіть на гілках із continue.
Помилка №3: while (true) без явно читабельного break.
Нескінченний цикл сам по собі не зло, але він має бути чесним: точку виходу має бути видно швидко, зазвичай у верхній частині тіла. Якщо break захований у глибині розгалужень, людина (і ви за два дні) перестає розуміти, як цикл узагалі закінчується. У таких ситуаціях краще або переписати умову в заголовок while (...), або перебудувати перевірки так, щоб break був очевидним.
Помилка №4: занадто багато «стрибків» в одному циклі.
Коли в циклі три continue і два break, код починає нагадувати гру «вгадай, де ти опинишся за секунду». Формально це може бути правильно, але читати й підтримувати важко. Зазвичай допомагає перестановка перевірок на початку ітерації та спрощення умов: менше вкладеності, більше прямого сенсу.
Помилка №5: використовувати toInt() замість toIntOrNull() у циклі введення.
Якщо ви робите введення в циклі, користувач майже гарантовано колись введе не те: пробіл, порожній рядок, «пʼять», «12.3», «-». toInt() у такому місці призводить до аварійного завершення програми, тобто ваш цикл навіть не встигне «ввічливо перепитати». Для стійкого введення використовуйте toIntOrNull(), а неправильне введення обробляйте через continue, щоб цикл просто попросив ввести ще раз.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ