Циклы с условием. Циклы по переменной.
До этого мы писали программы, в которых каждая команда выполнялась только один раз или не выполнялась вообще. Но мощь компьютера состоит ещё и в том, что короткая программа может выполнять очень сложную и длительную обработку данных, применяя некоторые команды многократно.
Цикл — это многократное выполнение одинаковых действий.
Поскольку цикл связан с повторением, циклические алгоритмы называют итерационными (от лат. iteratio — повторение). Каждое выполнение тела цикла называют итерацией.
Допустим, мы хотим вывести 5 раз на экран слово «привет». Можно, конечно, записать 5 одинаковых команд:
print( "привет" )
print( "привет" )
print( "привет" )
print( "привет" )
print( "привет" )
Но если нужно будет сделать какие-то действия 1000 или 1 000 000 раз? В этом случае можно организовать цикл. Простейший цикл, нужный нам в этой задаче, мы хотели бы записать так:
# сделай 5 раз:
print( "привет" )
К сожалению, в Python (как и во многих других языках программирования) такого цикла нет1*. Однако можно запрограммировать те же действия немного по-другому. Давайте разберёмся, как можно организовать цикл на языке Python.
Вы знаете, что программа выполняется автоматически. И при этом в любой момент нужно знать, сколько раз уже выполнен цикл и сколько ещё осталось выполнить. Для этого необходимо использовать ячейку памяти (переменную). В ней можно, например, запоминать количество завершённых итераций цикла. Такая переменная целого типа часто называется счётчиком.
Сначала в переменную-счётчик записывают ноль (ни одной итерации не сделано), а после каждой итерации цикла увеличивают значение на единицу:
count = О
while count < 5: # заголовок цикла
print( "привет" )
count += 1 # увеличение счётчика
В этой программе используется новое служебное слово while (в переводе с английского — «пока»), после которого записано условие.
Все операторы, которые выполняются в цикле (они называются телом цикла), сдвигаются вправо на одинаковое число позиций, так же как и в условном операторе. Этот приём позволяет обойтись без операторных скобок, ограничивающих тело цикла в других языках программирования.
Нам нужно выполнять цикл 5 раз, т. е. пока счётчик не станет равен 5. Об этом говорит заголовок цикла
while count < 5:Его можно прочитать как «делай, пока count < 5».
После каждой итерации цикла переменная count увеличивается на 1 — цикл выполнен ещё один раз. Если программист забудет написать этот оператор, произойдёт зацикливание: программа никогда не остановится, потому что условие count < 5 никогда не станет ложным.
Цикл можно построить и по-другому: сразу записать в счётчик нужное количество итераций, и после каждой итерации цикла уменьшать счётчик на 1. Тогда цикл должен закончиться при нулевом значении счётчика:
count = 5
while count > 0: # заголовок цикла
print( "привет" )
count -= 1. # уменьшение счётчика
Этот вариант несколько лучше, чем предыдущий, поскольку счётчик сравнивается с нулём, а такое сравнение выполняется в процессоре автоматически.
Цикл, в котором проверка условия выполняется при входе (перед выполнением очередной итерации) называется циклом с предусловием, т. е. циклом с предварительной проверкой условия. Перед тем как начать выполнение цикла, мы проверяем, нужно ли это делать вообще.
Все циклы, записанные в начале параграфа, — это циклы с предусловием. У них есть два важных свойства:
- условие проверяется при входе в цикл, поэтому тело цикла не выполнится ни разу, если условие в самом начале ложно;
- когда при очередной проверке условия в заголовке цикла выясняется, что это условие ложно, работа цикла заканчивается.
Пример
Рассмотрим ещё одну задачу, которая решается с помощью цикла с условием. Требуется ввести с клавиатуры натуральное число и найти сумму цифр его десятичной записи. Например, если ввели число 123, программа должна вывести сумму 1 + 2 + 3 = 6.
Сначала составим алгоритм решения этой задачи. Предположим, что число записано в переменной N. Нам нужно как-то разбить число на отдельные цифры.
Текущую цифру числа будем хранить в переменной digit.
Вспомним, что остаток от деления числа на 10 равен последней цифре его десятичной записи. Запишем эту цифру в переменную digit:
digit = N % 10Сумму цифр будем хранить в целой переменной summa. В самом начале, пока ни одну цифру ещё не обработали, значение этой переменной равно нулю:
summa = ОДля того чтобы добавить к предыдущей сумме новую цифру, нужно заменить значение переменной summa на summa+digit, т. е. выполнить присваивание
summa += digitДля того чтобы получить следующую цифру числа, надо затем отсечь последнюю цифру числа N. Для этого разделим N на 10 (основание системы счисления):
N = N // 10Эти три операции — выделение последней цифры числа, увеличение суммы и отсечение последней цифры — нужно выполнять несколько раз, пока все цифры не будут обработаны (и отсечены!) и в переменной N не останется ноль:
N = int( input( "Введите число: " ) )
summa = О
while N != 0:
digit = N % 10
summa += digit
N = N // 10
print("Сумма цифр", summa)
Выполним трассировку (ручную прокрутку) программы при N = 15. В столбцы таблицы будем записывать изменение значений всех переменных:

Для введённого числа 15 программа выведет ответ 6 (последнее значение переменной summa).
В отличие от предыдущего примера здесь количество итераций цикла заранее неизвестно, ведь оно определяется введённым числом (точнее — количеством цифр в его десятичной записи).
Докажем, что эта программа не зациклится, т. е. не будет работать бесконечно. Цикл завершается, когда переменная N становится равна нулю, поэтому нужно доказать, что это обязательно случится. По условию заданное число — натуральное, на каждой итерации цикла оно делится на 10 (остаток отбрасывается). В результате после очередного деления оно обязательно станет равно нулю.
Алгоритм Евклида — это один из самых древних известных алгоритмов, который используется по сей день. Его автор — греческий математик Евклид — жил в III веке до нашей эры. Алгоритм Евклида позволяет найти наибольший общий делитель (НОД) двух натуральных чисел.
Алгоритм Евклида для натуральных чисел: заменять большее из двух заданных чисел на их разность до тех пор, пока они не станут равны. Полученное число и есть их НОД.
Этот вариант алгоритма работает довольно медленно, если одно из чисел значительно меньше другого, например, для пары чисел 2 и 2014. Значительно быстрее выполняется улучшенный (модифицированный) алгоритм Евклида.
Модифицированный алгоритм Евклида для натуральных чисел: заменять большее из двух заданных чисел на остаток от деления большего на меньшее, пока этот остаток не станет равен нулю. Тогда второе число и есть их НОД.
Мы видим, что здесь тоже нужно выполнять некоторые операции несколько раз, причём сколько раз — заранее неизвестно. Но нас выручит цикл с условием, ведь мы знаем, когда нужно остановиться — когда какое-нибудь из двух чисел станет равно нулю. Значит, нужно выполнять цикл, пока оба значения (одновременно!) ненулевые:
while а != 0 and b != 0:
if а > b:
а = а % b
else:
b = b % а
Остаётся вывести результат — ненулевое значение переменной а или Ь. Можно было бы написать вывод так:
if а != 0:
print( а )
else:
print( b )
Однако можно использовать тот факт, что если одно из двух чисел равно нулю, то их сумма равна второму (ненулевому) числу. Приходим к такому варианту:
print ( a+b )Количество итераций такого цикла заранее неизвестно и зависит от исходных данных. Дополним программу так, чтобы она считала ещё и количество сделанных итераций цикла. Для этого нужно ввести переменную-счётчик целого типа. Перед началом цикла счётчик обнуляется (в него записывается ноль), и после каждой итерации цикла значение счётчика увеличивается на единицу:
count = О
while а != 0 and b != 0:
...
count += 1
print ( a+b )
print ( "Шагов:", count )
Вместо многоточия нужно вставить условный оператор, как и в предыдущем варианте программы. Обратите внимание, что оператор, увеличивающий значение счётчика, записывается с отступом — он находится в теле цикла.
Рассмотрим такую задачу: на вход программы поступает поток данных — последовательность целых чисел, которая заканчивается нулём. Требуется найти сумму элементов этой последовательности.
В этой задаче не нужно сохранять все данные в памяти, мы можем добавлять их к сумме по одному. Будем использовать две целые переменные: в переменной х будем хранить последнее введённое число, а в переменной summa — накапливать сумму.
Сначала запишем основной цикл программы, «скрыв» шаги алгоритма в комментариях:
while х != 0:
# добавить х к сумме
# прочитать следующее число
Однако перед таким циклом нужно прочитать первое число, иначе неясно, откуда возьмётся значение х при первой проверке условия. В итоге получается такая программа:
summa = О
х = int( input () )
while х != 0:
summa += х
х = int( input() )
print ( "Сумма", summa )
В некоторых задачах используются циклы, условие которых всегда истинно (многоточие обозначает тело цикла):
while True:
...
Поскольку условие True всегда истинно, такой цикл никогда не завершится. Но завершить его всё-таки можно, если применить специальный оператор break. Например, недостаток предыдущей программы обработки потока состоит в том, что команда ввода очередного числа в программе записана дважды: перед началом цикла и в конце тела цикла. А можно сделать так:
sum = О
while True:
х = int(input ())
if x == 0:
break # досрочный выход из цикла
sum += х
print ( "Сумма", sum )
Мы заранее (до цикла) ничего не вводим, и организуем цикл по другому. Вводится очередное число и сразу проверяется: если оно равно 0, цикл завершается с помощью оператора break. Если это не ноль, число добавляется к сумме и цикл работает дальше.
Вернёмся снова к задаче, которую мы обсуждали в одном из параграфов — вывести на экран несколько раз слово «привет». Фактически нам нужно организовать цикл, в котором блок операторов выполнится заданное число раз. Для этого можно применить ещё один вид цикла — цикл по переменной (или цикл с параметром). На языке Python он записывается так:
for i in range(10):
print( "привет" )
Здесь слово for означает «для», переменная i (её называют переменной цикла) изменяется в диапазоне (in range) от 0 до 10, не включая 10 (т. е. от 0 до 9 включительно). Таким образом, цикл выполняется для i = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 — ровно 10 раз. Переменная i — это счётчик выполненных итераций цикла. Можно было записать этот цикл и по-другому:
for i in [0,1,2,3,4,5,б,7,8,9]:
print( "привет" )
В квадратных скобках через запятую перечислены все значения переменной, при которых выполняется цикл. Если их много, такой способ неудобен, лучше использовать встроенную функцию range.
Обратите внимание, что последовательность, которую строит функция range, не бесконечна, т. е. цикл по переменной всегда заканчивается, программа не может зациклиться.
Рассмотрим ещё один пример. В информатике важную роль играют степени числа 2 (2, 4, 8, 16 и т. д.). Давайте выведем на экран все степени двойки от 21 до 210. Для решения этой задачи мы можем написать программу, использующую цикл с условием:
power = 1
N = 2
while power <= 10:
print(N)
N *= 2
power += 1
Вы, наверно, заметили, что переменная power используется трижды (см. выделенные блоки): в операторе присваивания начального значения, в условии выполнения цикла и в теле цикла (увеличение на 1).
Чтобы собрать все действия с ней в один оператор, применим цикл по переменной. Нам нужно выполнить тело цикла при power = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10. Чтобы получить такой набор значений, нужно вызвать функцию range с двумя аргументами: первый — это начальное значение (1), а второй — ограничитель, не входящий в последовательность (11):
N = 2
for power in range(1,11):
print(N)
N *= 2
Запись цикла получилась проще, и поэтому меньше шансов сделать ошибку.
Однако не любой цикл с условием может быть переписан как цикл по переменной. Если количество повторения цикла неизвестно и не может быть найдено заранее (как в задаче с вычислением суммы цифр числа), цикл по переменной использовать не удаётся.
Вместе с тем, любой цикл по переменной можно заменить на равносильный цикл с условием: вместо вызова функции range придётся задать отдельно начальное значение переменной цикла, условие продолжения цикла и правило изменения переменной цикла.
Рассмотрим ещё одну задачу — найдём сумму всех натуральных чисел от 1 до 1000. Для накопления суммы будем использовать переменную (назовём её summa). В цикле другая переменная (скажем, i) изменяется от 1 до 1000, и на каждом шаге этого цикла к сумме добавляется очередное число:
summa = О
for i in range (1,1001) :
summa += i
По умолчанию функция range строит последовательность, в которой каждое следующее число на 1 больше предыдущего. Но это правило можно изменить, если при вызове функции range указать третий аргумент — шаг изменения переменной цикла. Следующая программа выводит квадраты натуральных чисел от 10 до 1 в порядке убывания:
for k in range (10,0,-1) :
print(к*к)
В этом примере шаг равен -1, т. е. каждое следующее число на 1 меньше предыдущего. Заметим, что конечное значение 0 не входит в последовательность.
Пусть, например, нам нужно перебрать в цикле все значения переменной i от 0 до 100, кратные пяти: 0, 5, 10, …, 100. Для этого нужно взять шаг изменения переменной, равный 5:
for i in range(0, 101, 5):
... # что-то делать с i
Второй аргумент функции range равен 101 для того, чтобы последнее значение переменной i было равно 100. Значение-ограничитель должно быть больше, чем 100 (чтобы число 100 появилось в последовательности), но меньше, чем 106 (чтобы следующее число, 105, не появилось).
- С помощью циклов в программе можно выполнять повторяющиеся действия.
- Цикл с условием выполняется до тех пор, пока некоторое условие (условие продолжения работы цикла) не станет ложным.
- Если условие в заголовке цикла ложно с самого начала, тело цикла не выполнится ни разу.
- Если условие в заголовке цикла всегда остаётся истинным, цикл работает бесконечно — программа зацикливается.
- Модифицированный алгоритм Евклида для вычисления НОД двух натуральных чисел: заменять большее из чисел на остаток от деления большего на меньшее, пока этот остаток не станет равен нулю. Тогда второе число и есть их НОД.
- Для досрочного выхода из цикла используют оператор break.
- Цикл по переменной применяют тогда, когда количество итераций цикла заранее известно или может быть вычислено до начала цикла.
- Переменная, изменение которой определяет работу цикла, называется переменной цикла.
- Цикл по переменной всегда заканчивается (программа не может зациклиться).
- Цикл с условием, для которого количество итераций известно или может быть вычислено заранее, можно заменить на равносильный цикл по переменной.
- Любой цикл по переменной можно заменить на цикл с условием, который выполняет те же действия.
- При вызове функции range указывают начальное значение, значение-ограничитель (не входящее в последовательность) и шаг изменения переменной цикла.