Основы программирования
Циклы
**Загадка:** Как вывести числа от 1 до 100 без написания 100 строк print()? И как найти сумму всех этих чисел? Ответ - циклы. Они дают возможность одному блоку кода выполняться тысячи раз.
Компьютеры созданы для рутины. Обработать миллион записей, проверить каждый символ пароля, отправить уведомление всем пользователям - всё это циклы.
Цели урока
- Освоить цикл for для перебора
- Понять цикл while для условных повторений
- Научиться управлять циклами (break, continue)
- Избегать бесконечных циклов
Предварительные знания
- Условные операторы (урок 6)
- Операторы сравнения (урок 4)
Instagram обрабатывает миллиарды фото в день. YouTube проверяет каждый загруженный кадр. Spotify анализирует миллионы треков. Всё это - циклы в масштабе.
- **Email рассылка**: for user in users: send_email(user)
- **Поиск**: for item in catalog: if matches(item): return item
- **Игры**: while player.is_alive: game_loop()
- **Бекап**: for file in files: backup(file)
От DO-цикла FORTRAN до for-each в Java и range-for в C++
Первый настоящий цикл в языке высокого уровня появился в FORTRAN I в 1957 году: DO 10 I = 1, 100. Метка 10 указывала на последнюю строку тела, а DO повторял блок 100 раз. Это была прямая обёртка над ассемблерным счётчиком цикла. В ALGOL 60 (1960) добавили while-цикл и for с диапазоном и шагом, более выразительные, чем FORTRAN. В 1972 году Деннис Ритчи в C ввёл общую форму for(init; cond; step), где все три части это произвольные выражения. Эта схема перешла в C++, Java, JavaScript, PHP и стала каноном на десятилетия. Проблема: перебор коллекций через индекс был многословным и подверженным ошибкам off-by-one. Smalltalk-80 (1980) и потом Python (1991) предложили простой for-each: for x in collection. В мае 2004 года Java 5 добавила enhanced for: for(Type x: collection) и тем самым окончательно ввела for-each в мейнстрим. В августе 2011 года C++11 догнал миром: range-based for: for(auto& x: container) на итераторах begin/end. Современные языки идут дальше: Python comprehensions, Kotlin forEach, Rust for x in iter, Swift for x in seq. Идея одна: разработчик описывает что перебрать, а не как двигать счётчик.
Цикл for: перебор коллекций
**for** - цикл для перебора элементов. "Для каждого элемента в коллекции - сделай что-то".
Базовый синтаксис
Перебор списка
```python fruits = ["яблоко", "банан", "вишня"] for fruit in fruits: print(f"Мне нравится {fruit}") # Вывод: # Мне нравится яблоко # Мне нравится банан # Мне нравится вишня ``` **Как читать:** "для каждого fruit в fruits выполни print"
Что можно перебирать?
Разные типы данных
```python # Список for num in [1, 2, 3]: print(num) # Строку (посимвольно) for char in "Hello": print(char) # H, e, l, l, o # Словарь (по ключам) for key in {"a": 1, "b": 2}: print(key) # a, b ```
Сколько раз выполнится print в коде? ```python for x in [5, 10, 15]: print(x) ```
range() - генератор чисел
**range()** генерирует последовательность чисел. Идеально для "сделай N раз".
Варианты range()
Разные способы использования
```python # range(stop) - от 0 до stop-1 for i in range(5): print(i) # 0, 1, 2, 3, 4 # range(start, stop) - от start до stop-1 for i in range(1, 6): print(i) # 1, 2, 3, 4, 5 # range(start, stop, step) - с шагом for i in range(0, 10, 2): print(i) # 0, 2, 4, 6, 8 # Обратный отсчёт for i in range(5, 0, -1): print(i) # 5, 4, 3, 2, 1 ```
Ответ на загадку!
Сумма чисел от 1 до 100
```python # Вывод чисел от 1 до 100 for i in range(1, 101): print(i) # Сумма чисел от 1 до 100 total = 0 for i in range(1, 101): total += i print(f"Сумма: {total}") # 5050 ``` Гаусс в детстве вычислил это за секунды: `(1 + 100) * 100 / 2 = 5050`
**range() ленив** - он не создаёт список сразу, а генерирует числа по мере необходимости. Поэтому `range(1_000_000_000)` не займёт память!
Что выведет: list(range(2, 8, 2))?
Перебор строк и enumerate
Строки - это последовательности символов. Их тоже можно перебирать!
Посимвольный перебор
Каждый символ отдельно
```python word = "Python" for char in word: print(char) # P, y, t, h, o, n # Практика: подсчёт гласных vowels = "aeiouAEIOU" count = 0 for char in "Hello World": if char in vowels: count += 1 print(f"Гласных: {count}") # 3 ```
enumerate() - индекс + значение
Когда нужен номер элемента
```python fruits = ["яблоко", "банан", "вишня"] # Без enumerate for i in range(len(fruits)): print(f"{i}: {fruits[i]}") # С enumerate - элегантнее! for i, fruit in enumerate(fruits): print(f"{i}: {fruit}") # Начать с 1: for i, fruit in enumerate(fruits, start=1): print(f"{i}. {fruit}") # 1. яблоко # 2. банан # 3. вишня ```
Что выведет? ```python for i, c in enumerate("AB"): print(i, c) ```
Цикл while: пока условие истинно
**while** - цикл, который выполняется пока условие True. Идеален, когда не знаешь заранее сколько раз повторять.
Базовый while
Повторяй пока условие истинно
```python count = 0 while count < 5: print(f"Итерация {count}") count += 1 # ВАЖНО: изменяем условие! # Итерация 0 # Итерация 1 # ... # Итерация 4 ```
**Опасность!** Если условие никогда не станет False - получишь бесконечный цикл. Всегда убеждайся, что условие изменяется!
Практические примеры
Когда while лучше for
```python # Игровой цикл health = 100 while health > 0: damage = random.randint(1, 20) health -= damage print(f"Урон: {damage}, HP: {health}") print("Game Over") # Ввод с валидацией password = "" while len(password) < 8: password = input("Пароль (мин. 8 символов): ") print("Пароль принят!") # Угадай число secret = 42 guess = 0 while guess != secret: guess = int(input("Угадай число: ")) print("Верно!") ```
while всегда выполнится хотя бы раз
while проверяет условие ДО первой итерации
Если условие сразу False, тело цикла не выполнится ни разу. while False: print('Hi') - ничего не выведет.
Сколько раз выведется "Hi"? ```python x = 10 while x > 5: print("Hi") x -= 2 ```
break и continue: управление циклом
**break** - немедленный выход из цикла. **continue** - пропуск текущей итерации, переход к следующей.
break - выход из цикла
Прерывание при условии
```python # Поиск первого чётного numbers = [1, 3, 5, 4, 7, 8] for num in numbers: if num % 2 == 0: print(f"Найдено: {num}") break # Выходим, не проверяя остальные # Выведет только: Найдено: 4 ```
continue - пропуск итерации
Пропустить и продолжить
```python # Вывести только положительные numbers = [1, -2, 3, -4, 5] for num in numbers: if num < 0: continue # Пропускаем отрицательные print(num) # 1, 3, 5 ```
while True + break
Популярный паттерн
```python # Меню с выходом while True: command = input("Команда (q=выход): ") if command == "q": print("До свидания!") break print(f"Выполняю: {command}") ```
Что выведет? ```python for i in range(5): if i == 2: continue print(i) ```
Вложенные циклы
Циклы можно вкладывать друг в друга. Это полезно для работы с матрицами, таблицами, декартовым произведением.
Таблица умножения
Классический пример
```python for i in range(1, 4): # Внешний цикл: строки for j in range(1, 4): # Внутренний: столбцы print(f"{i}×{j}={i*j}", end=" ") print() # Новая строка # 1×1=1 1×2=2 1×3=3 # 2×1=2 2×2=4 2×3=6 # 3×1=3 3×2=6 3×3=9 ```
Все пары
Декартово произведение
```python colors = ["красный", "синий"] sizes = ["S", "M", "L"] for color in colors: for size in sizes: print(f"{color} {size}") # красный S, красный M, красный L # синий S, синий M, синий L ```
**Сложность вложенных циклов**: два цикла по N элементов = N × N операций. Три цикла = N³. Это быстро становится медленным! Избегай глубокой вложенности.
Сколько раз выведется "X"? ```python for i in range(3): for j in range(2): print("X") ```
Связь с другими темами
Циклы соединяются почти со всеми коллекциями и операциями:
Итог
- for перебирает любую коллекцию: список, строку, словарь, range, генератор
- range(start, stop, step) генерирует числа лениво, не занимает память на больших значениях
- while повторяется пока условие True, идеален когда заранее не знаешь сколько раз нужно
- break прерывает цикл целиком, continue пропускает только текущую итерацию
- enumerate(seq) даёт пары (индекс, значение), zip(a, b) идёт по двум коллекциям параллельно
- Вложенность циклов умножает работу: два цикла по N это N*N операций, следи за сложностью
Вопросы для размышления
- В чём ключевая разница между циклом с предусловием и циклом с постусловием по семантике выполнения?
- Когда рекурсия семантически чище цикла, а когда цикл - рекурсии?
- Как бесконечный цикл с явным break отличается от цикла while по читаемости и намерению?
Связанные уроки
- prog-06-conditionals — Цикл опирается на условие чтобы решить когда остановиться
- prog-09-recursion — Рекурсия это повторение через вызовы самой себя
- prog-11-arrays — Циклы это основной способ обходить массивы
- alg-09-linear-search — Линейный поиск это цикл по каждому элементу
- prob-02-combinatorics — Подсчёт итераций связывает циклы с комбинаторикой
- alg-01-big-o