8. (Л) Цикли for і while та їх застосування¶
Зміст лекції¶
- Навіщо потрібні цикли
- Два види циклів у Python
- Цикл
forта функціяrange() - Перебір рядків і колекцій
- Цикл
while - Лічильники та накопичувачі
break,continueта порожній операторpass- Гілка
elseу циклі - Вкладені цикли
- Корисні супутники циклів:
enumerate, агрегатні функції - Типові застосування циклів
- Типові помилки
Навіщо потрібні цикли¶
Уявіть, що треба вивести числа від 1 до 5. Із тим, що ми вже знаємо, вихід один:
А якщо до 1000? А якщо кількість чисел стане відома лише під час роботи програми — користувач введе її з клавіатури? Копіювати рядки неможливо в принципі.
Цикл — це конструкція, яка виконує один і той самий блок коду багато разів. Один прохід циклу називають ітерацією.
П'ять рядків перетворилися на два — і, що важливіше, замінивши 6 на 1001, ми отримаємо тисячу рядків без жодного зайвого символу коду.
graph TD
S["Початок"] --> C{"Є ще<br/>дані?"}
C -->|"Так"| B["Тіло циклу"]
B --> C
C -->|"Ні"| E["Кінець"]
style S fill:#339af0,stroke:#333,color:#fff
style C fill:#ffd43b,stroke:#333,color:#000
style B fill:#ff922b,stroke:#333,color:#000
style E fill:#51cf66,stroke:#333,color:#000
Два види циклів у Python¶
| Цикл | Коли використовувати | Ключове питання |
|---|---|---|
for |
кількість повторень відома заздалегідь або треба перебрати всі елементи послідовності | «для кожного елемента…» |
while |
повторювати, поки виконується умова; кількість повторень наперед невідома | «поки істинно…» |
Класичний приклад різниці: вивести 10 чисел — це for (рівно 10 разів). Запитувати пароль, доки користувач не введе правильний, — це while (може знадобитися і одна спроба, і сорок).
Будь-який for можна переписати через while
І навпаки — майже завжди. Але правильно обраний цикл робить код коротшим і зрозумілішим, тому вибір не формальний, а змістовний.
Цикл for та функція range()¶
Синтаксис¶
graph LR
K["for<br/>ключове слово"] --> V["змінна<br/>циклу"]
V --> I["in<br/>послідовність"]
I --> D["двокрапка<br/>:"]
D --> B["блок коду<br/>з відступом"]
style K fill:#339af0,stroke:#333,color:#fff
style V fill:#ffd43b,stroke:#333,color:#000
style I fill:#ffd43b,stroke:#333,color:#000
style D fill:#ff922b,stroke:#333,color:#000
style B fill:#51cf66,stroke:#333,color:#000
Цикл for перебирає елементи послідовності по одному і на кожній ітерації записує черговий елемент у змінну циклу.
Тут letter послідовно набуває значень "c", "o", "d", "e". Змінну циклу оголошувати заздалегідь не потрібно — Python створює її сам.
Функція range()¶
Найчастіше for використовують разом із range() — вона породжує послідовність цілих чисел. Має три форми:
| Виклик | Що дає | Приклад значень |
|---|---|---|
range(stop) |
від 0 до stop - 1 |
range(5) → 0 1 2 3 4 |
range(start, stop) |
від start до stop - 1 |
range(2, 6) → 2 3 4 5 |
range(start, stop, step) |
з кроком step |
range(0, 10, 3) → 0 3 6 9 |
# Program: demonstrate the three forms of range()
print("range(5):")
for i in range(5):
print(i, end=" ")
print()
print("range(2, 6):")
for i in range(2, 6):
print(i, end=" ")
print()
print("range(0, 10, 3):")
for i in range(0, 10, 3):
print(i, end=" ")
print()
Параметр end у print()
За замовчуванням print() завершує вивід переходом на новий рядок. end=" " замінює цей перехід пробілом — так значення друкуються в один рядок. Порожній print() після циклу переводить курсор на новий рядок.
Права межа не входить у діапазон
range(1, 10) — це числа від 1 до 9, а не до 10. Це найчастіша помилка новачків (її називають off-by-one, «помилка на одиницю»). Щоб отримати числа від 1 до 10 включно, пишіть range(1, 11).
Зворотний відлік¶
Крок може бути від'ємним — тоді start більший за stop:
Коли змінна циклу не потрібна¶
Якщо всередині тіла значення лічильника не використовується, за домовленістю пишуть підкреслення _:
Кількість ітерацій¶
Кількість повторень for фіксується на початку циклу. Змінити її, присвоївши щось змінній циклу всередині тіла, неможливо:
Цикл усе одно виконався рівно 5 разів: на кожній ітерації range підставляє в i наступне своє значення, затираючи те, що ми туди записали.
Перебір рядків і колекцій¶
Цикл for працює з будь-яким ітерованим об'єктом. Поки що нам знайомі рядки; крім них, у Python є списки та кортежі — докладно про них буде далі в курсі, але перебирати їх можна вже зараз.
# Program: iterate over a string, a tuple and a list
word = "Python"
for character in word:
print(character, end="-")
print()
for day in ("Mon", "Tue", "Wed"):
print(day)
grades = [90, 84, 77]
for grade in grades:
print(f"Grade: {grade}")
Рядок можна перебирати і за індексами — через range(len(...)). Цей спосіб потрібен, коли важлива саме позиція символу:
Що обрати
Якщо потрібні лише значення — перебирайте безпосередньо (for c in word). range(len(...)) беріть тільки тоді, коли справді потрібен індекс. Прямий перебір читабельніший і не дає помилитися з межами.
Цикл while¶
while повторює блок доти, доки умова істинна. Умова перевіряється перед кожною ітерацією.
graph TD
I["count = 1"] --> C{"count <= 5"}
C -->|"True"| B["print(count)<br/>count += 1"]
B --> C
C -->|"False"| E["print: Done"]
style I fill:#339af0,stroke:#333,color:#fff
style C fill:#ffd43b,stroke:#333,color:#000
style B fill:#ff922b,stroke:#333,color:#000
style E fill:#51cf66,stroke:#333,color:#000
Кожен коректний while складається з трьох частин. Забути будь-яку з них — типова помилка:
- Ініціалізація — змінна умови існує ще до циклу (
count = 1). - Умова — логічний вираз у заголовку (
count <= 5). - Зміна — у тілі щось наближає умову до хибності (
count += 1).
Нескінченний цикл¶
Якщо третій пункт випав, цикл не завершиться ніколи:
# УВАГА: нескінченний цикл — не запускайте без потреби
# count = 1
# while count <= 5:
# print(count) # count не змінюється -> умова істинна завжди
Як зупинити програму, що зациклилася
У терміналі натисніть Ctrl + C. Це перерве виконання й покаже KeyboardInterrupt. Якщо цикл ще й друкує в консоль, вивід може «залити» екран — це нормально, Ctrl + C все одно спрацює.
Коли while природніший за for¶
Головний випадок — перевірка введення користувача. Скільки разів людина помилиться, наперед не знає ніхто:
# Program: keep asking until the user enters a positive number
number = int(input("Enter a positive number: "))
while number <= 0:
print("Error: the number must be greater than zero")
number = int(input("Enter a positive number: "))
print(f"Accepted: {number}")
Enter a positive number: -4
Error: the number must be greater than zero
Enter a positive number: 0
Error: the number must be greater than zero
Enter a positive number: 12
Accepted: 12
Другий випадок — коли кінець роботи визначається самими даними, а не лічильником:
# Program: reverse the digits of a positive integer
number = int(input("Enter a positive integer: "))
reversed_number = 0
while number > 0:
digit = number % 10 # остання цифра
reversed_number = reversed_number * 10 + digit
number = number // 10 # відкидаємо останню цифру
print(f"Reversed: {reversed_number}")
while True і навмисна нескінченність¶
Іноді умову виходу зручніше перевіряти в середині тіла. Тоді пишуть завідомо істинну умову й виходять оператором break:
# Program: sum numbers until the user types "stop"
total = 0
while True:
line = input("Number (or 'stop'): ")
if line == "stop":
break
total += int(line)
print(f"Total: {total}")
Такий цикл нескінченний лише формально: вихід із нього гарантує break. Головне — переконатися, що умова break колись стане істинною.
Лічильники та накопичувачі¶
Більшість практичних задач із циклами зводяться до одного з двох шаблонів.
Накопичувач (акумулятор) — змінна, що збирає результат за всі ітерації. Її обов'язково ініціалізують до циклу: сума — нулем, добуток — одиницею.
# Program: sum of numbers from 1 to n and factorial of n
n = int(input("Enter n: "))
total = 0 # нейтральний елемент для додавання
product = 1 # нейтральний елемент для множення
for i in range(1, n + 1):
total += i
product *= i
print(f"Sum 1..{n} = {total}")
print(f"Factorial {n}! = {product}")
Ініціалізація всередині циклу
Лічильник — змінна, що рахує, скільки разів сталася подія:
# Program: count vowels in a word
word = input("Enter a word: ").lower()
vowels = 0
for character in word:
if character in "aeiou":
vowels += 1
print(f"Vowels: {vowels}")
Пошук мінімуму й максимуму — окремий випадок накопичувача. Початкове значення беруть із перших даних, а не «зі стелі»:
# Program: find the largest of n numbers entered from the keyboard
count = int(input("How many numbers? "))
largest = int(input("Number 1: "))
for i in range(2, count + 1):
value = int(input(f"Number {i}: "))
if value > largest:
largest = value
print(f"Largest: {largest}")
break, continue та порожній оператор pass¶
| Оператор | Дія |
|---|---|
break |
негайно завершує весь цикл |
continue |
пропускає решту тіла й переходить до наступної ітерації |
pass |
не робить нічого; заповнювач для порожнього блоку |
graph TD
C{"Умова циклу"} -->|"True"| B1["Початок тіла"]
B1 --> Q1{"break?"}
Q1 -->|"так"| E["Вихід із циклу"]
Q1 -->|"ні"| Q2{"continue?"}
Q2 -->|"так"| C
Q2 -->|"ні"| B2["Решта тіла"]
B2 --> C
C -->|"False"| E
style C fill:#ffd43b,stroke:#333,color:#000
style Q1 fill:#ff6b6b,stroke:#333,color:#fff
style Q2 fill:#ff922b,stroke:#333,color:#000
style E fill:#51cf66,stroke:#333,color:#000
break¶
# Program: find the first number divisible by 7 in a range
for number in range(20, 40):
if number % 7 == 0:
print(f"Found: {number}")
break
print(f"Checked: {number}")
Цикл зупинився на 21 — решта чисел до 39 не перевірялися взагалі. Це головна цінність break: не робити зайвої роботи.
continue¶
# Program: print odd numbers only
for number in range(1, 11):
if number % 2 == 0:
continue # парні пропускаємо
print(number, end=" ")
print()
continue у while — пастка
Якщо continue стоїть перед зміною лічильника, ця зміна ніколи не виконається і цикл зависне:
# НЕПРАВИЛЬНО: при i == 3 лічильник більше не зростає
# i = 0
# while i < 10:
# if i == 3:
# continue
# i += 1
У for такої проблеми немає — там лічильник змінює сам range.
Гілка else у циклі¶
У Python цикл може мати блок else. Він виконується, якщо цикл завершився природно — без жодного break.
# Program: check whether a number is prime
number = int(input("Enter an integer greater than 1: "))
for divisor in range(2, number):
if number % divisor == 0:
print(f"{number} is not prime: divisible by {divisor}")
break
else:
print(f"{number} is prime")
Читати else тут варто як «якщо нічого не знайшли». Конструкція зустрічається нечасто, але саме в задачах пошуку економить додаткову змінну-прапорець:
# те саме через прапорець — довше
number = 21
found = False
for divisor in range(2, number):
if number % divisor == 0:
found = True
break
if found:
print(f"{number} is not prime")
else:
print(f"{number} is prime")
Вкладені цикли¶
Тіло циклу може містити інший цикл. Внутрішній цикл повністю відпрацьовує на кожній ітерації зовнішнього.
# Program: multiplication table 1..3
for i in range(1, 4):
for j in range(1, 4):
print(f"{i} x {j} = {i * j}", end=" ")
print()
graph TD
O["Зовнішній цикл<br/>i = 1"] --> I1["Внутрішній: j = 1, 2, 3"]
I1 --> O2["Зовнішній цикл<br/>i = 2"]
O2 --> I2["Внутрішній: j = 1, 2, 3"]
I2 --> O3["Зовнішній цикл<br/>i = 3"]
O3 --> I3["Внутрішній: j = 1, 2, 3"]
style O fill:#339af0,stroke:#333,color:#fff
style O2 fill:#339af0,stroke:#333,color:#fff
style O3 fill:#339af0,stroke:#333,color:#fff
style I1 fill:#ffd43b,stroke:#333,color:#000
style I2 fill:#ffd43b,stroke:#333,color:#000
style I3 fill:#ffd43b,stroke:#333,color:#000
Загальна кількість виконань тіла внутрішнього циклу — добуток кількостей ітерацій. Три на три — дев'ять рядків виводу.
Другий класичний приклад — фігури з символів, де довжина рядка залежить від його номера:
# Program: print a triangle of stars
height = int(input("Height: "))
for row in range(1, height + 1):
for _ in range(row):
print("*", end="")
print()
break виходить лише з одного циклу
break у внутрішньому циклі завершує тільки внутрішній цикл; зовнішній продовжує роботу. Щоб перервати обидва, потрібен прапорець або окрема функція (про функції — далі в курсі).
Слідкуйте за кількістю ітерацій
Вкладеність множить роботу. Два цикли по 1000 ітерацій — це вже мільйон виконань тіла. Для навчальних задач це не проблема, але звичку оцінювати обсяг роботи варто виробити одразу.
Корисні супутники циклів¶
enumerate() — індекс і значення разом¶
Другим аргументом можна задати початок нумерації — зручно для списків, які показують людині:
Готові агрегатні функції¶
Частину типових циклів Python уже містить у вигляді вбудованих функцій:
grades = [90, 84, 77, 95]
print(sum(grades))
print(max(grades))
print(min(grades))
print(len(grades))
print(sum(grades) / len(grades))
Навчитися писати цикл усе одно треба
У практичних роботах суму й максимум часто просять обчислити вручну — саме щоб відпрацювати накопичувач. У реальному коді, звісно, беруть sum() і max().
Типові застосування циклів¶
1. Повторюваний запит із перевіркою¶
# Program: ask for a grade until a valid value is entered
while True:
value = input("Grade (0-100): ")
if not value.isdigit():
print("Error: digits only")
continue
grade = int(value)
if 0 <= grade <= 100:
break
print("Error: the value must be between 0 and 100")
print(f"Grade accepted: {grade}")
Grade (0-100): abc
Error: digits only
Grade (0-100): 145
Error: the value must be between 0 and 100
Grade (0-100): 87
Grade accepted: 87
2. Меню програми¶
# Program: simple text menu
while True:
print("1 - Say hello")
print("2 - Show a square")
print("0 - Exit")
choice = input("Your choice: ")
if choice == "1":
name = input("Your name: ")
print(f"Hello, {name}!")
elif choice == "2":
number = int(input("Number: "))
print(f"{number} ** 2 = {number ** 2}")
elif choice == "0":
print("Bye")
break
else:
print("Unknown choice, try again")
1 - Say hello
2 - Show a square
0 - Exit
Your choice: 2
Number: 9
9 ** 2 = 81
1 - Say hello
2 - Show a square
0 - Exit
Your choice: 0
Bye
3. Обробка тексту символ за символом¶
# Program: count digits, letters and other characters in a line
line = input("Enter a line: ")
digits = 0
letters = 0
others = 0
for character in line:
if character.isdigit():
digits += 1
elif character.isalpha():
letters += 1
else:
others += 1
print(f"Digits: {digits}")
print(f"Letters: {letters}")
print(f"Other characters: {others}")
4. Математичні обчислення з накопиченням¶
# Program: sum of digits of an integer
number = abs(int(input("Enter an integer: ")))
digit_sum = 0
while number > 0:
digit_sum += number % 10
number //= 10
print(f"Sum of digits: {digit_sum}")
Типові помилки¶
| Помилка | Причина | Виправлення |
|---|---|---|
| Програма «зависла» | у while нічого не змінює умову |
додати зміну лічильника в тілі |
TypeError: 'int' object is not iterable |
for i in 10: замість for i in range(10): |
обгорнути число в range() |
| Не той діапазон | забули, що range(1, 10) не містить 10 |
range(1, 11) |
NameError після циклу |
змінна створена лише в тілі, а цикл не виконався жодного разу | ініціалізувати змінну до циклу |
| Сума завжди дорівнює останньому числу | накопичувач обнуляється всередині циклу | винести total = 0 до циклу |
SyntaxError: 'break' outside loop |
break поза циклом |
прибрати або перенести в цикл |
IndentationError |
тіло циклу без відступу | зсунути тіло на 4 пробіли |
| Виконалася лише перша ітерація | зайвий break у тілі |
прибрати break або перенести під if |
# 1. цикл може не виконатися жодного разу
total = 0 # ініціалізація до циклу — обов'язкова
for i in range(0): # порожній діапазон
total += i
print(total)
# 2. число не є послідовністю
# for i in 5: -> TypeError: 'int' object is not iterable
for i in range(5):
pass
# 3. змінна циклу «живе» і після циклу
for i in range(3):
pass
print(i) # 2 — останнє значення
Порівняння дробових чисел в умові while
x = 0.0
# while x != 1.0: -> може не завершитися ніколи
# x += 0.1
while x < 1.0: # правильно: нерівність, а не рівність
x += 0.1
print(round(x, 2))
Число 0.1 не має точного представлення у двійковій системі, тому сума десяти таких доданків не дорівнює рівно 1.0 — вона трохи менша, і цикл робить ще одну, одинадцяту ітерацію. Саме тому умову виходу з циклу ніколи не будують на точній рівності дробових чисел: з != цей цикл не завершився б узагалі, а з < — завершується, але результат треба округлювати.
Підсумок¶
- Цикл повторює блок коду; один прохід називається ітерацією.
forперебирає елементи послідовності — використовуйте його, коли кількість повторень відома або треба обійти всі елементи.range(stop),range(start, stop),range(start, stop, step)породжують цілі числа; права межа не входить у діапазон.whileповторює блок, поки істинна умова. Коженwhileпотребує трьох речей: ініціалізації, умови й зміни змінної в тілі.- Нескінченний цикл зупиняється комбінацією Ctrl + C;
while Trueразом ізbreak— легальний і поширений прийом. - Накопичувач (
total = 0,product = 1) та лічильник ініціалізують до циклу. breakперериває весь цикл,continue— лише поточну ітерацію; уwhilecontinueлегко створює зациклення.- Блок
elseу циклі виконується, якщо не булоbreak— зручно в задачах пошуку. - Вкладені цикли: внутрішній повністю відпрацьовує на кожній ітерації зовнішнього, кількість виконань перемножується.
Корисні посилання¶
- Цикли
for— документація Python - Функція
range() break,continueтаelseу циклах- Оператор
while— довідник мови enumerate()таzip()- PEP 8 — оформлення коду
Домашнє завдання¶
Мета — навчитися свідомо обирати вид циклу й бачити межі його роботи.
-
Створіть файл
loops_range.pyі дослідним шляхом заповніть таблицю: для викликівrange(5),range(1, 5),range(5, 1),range(5, 1, -1),range(0, 10, 4)іrange(10, 0)випишіть, які числа виводить цикл і скільки ітерацій відбулося. Двома реченнями поясніть, чому два з цих викликів не дають жодної ітерації. -
Напишіть програму, яка виводить числа від 1 до 10, двома способами: через
forі черезwhile. Порівняйте обидві версії за кількістю рядків і за кількістю місць, де можна помилитися. У звіті вкажіть, який варіант ви обрали б для цієї задачі та чому. -
Навмисно створіть нескінченний цикл
while, запустіть його, зупиніть комбінацією Ctrl + C і збережіть знімок екрана з повідомленнямKeyboardInterrupt. Поясніть одним реченням, якої саме з трьох обов'язкових частинwhileбракувало вашому циклу. -
Візьміть приклад із розділу «Ініціалізація всередині циклу», запустіть обидві версії (з
total = 0до циклу і всередині нього) та поясніть, чому неправильна версія не викликає жодної помилки, але дає результат5замість15. Сформулюйте загальне правило про місце ініціалізації накопичувача. -
Напишіть програму, яка запитує ваш рік народження і виводить усі роки від вашого народження до поточного, у яких ви святкували день народження в парному віці. Використайте
range()з кроком і перевірте результат на власних даних.
Знайшли помилку чи бажаєте додати інформацію, щоб покращити курс? Створіть issue на GitHub