Основы программирования

Циклы

**Загадка:** Как вывести числа от 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") ```

Связь с другими темами

Циклы соединяются почти со всеми коллекциями и операциями:

  • Функции — Вынос тела цикла в функцию даёт map, filter, reduce
  • Рекурсия — Альтернатива циклу: повторение через вызов функцией самой себя
  • Массивы — Главный потребитель циклов: обход, фильтрация, агрегация
  • Объекты — Перебор ключей, значений, пар через items, keys, values

Итог

  • 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
Циклы

0

1

Войти