Как эмулировать цикл Do-While в Python?

Людям, которые перешли в Python с языков вроде C, C++, Java или JavaScript, может не хватать имеющейся там конструкции цикла do-while. Цикл do-while представляет собой обычную инструкцию программного потока, которая выполняет содержащийся в ней блок кода как минимум один раз, независимо от того, каким будет условие цикла – истинным или ложным. Эта особенность связана с тем, что условие цикла вычисляется в конце каждой итерации. То есть, первая итерация выполняется всегда.

Одним из наиболее популярных сценариев использования циклов этого типа является приём входных данных от пользователя и их обработка. Рассмотрим пример, написанный на языке C:

#include <stdio.h>

int main() {
    int number;
    do {
        printf("Введите любое положительное число: ");
        scanf("%d", &number);
        printf("%d\n", number);
    } while (number > 0);
    return 0;
}

В этой небольшой программе запускается цикл do … while, где от пользователя требуется ввести положительное число. Введённые данные сохраняются в переменной number и выводятся на экран. Операции в цикле продолжаются до тех пор, пока пользователь не введёт какое-нибудь отрицательное число.

Если вы скомпилируете и запустите эту программу, то получите следующий результат:

Введите любое положительное число: 1
1
Введите любое положительное число: 4
4
Введите любое положительное число: -1
-1

Условие (number > 0) вычисляется в конце цикла, и это гарантирует, что содержимое конструкции будет выполнено минимум один раз. Этим циклы do-while отличаются от обычных циклов while, где условие цикла проверяется в начале. В цикле while гарантии выполнения кода внутри цикла нет. Если условие цикла изначально имеет значение False, то код вообще не будет выполняться.

Примечание: В этом руководстве под условием цикла подразумевается условие, которое управляет циклом while или do-while. Не путайте условие с телом цикла, которое представляет собой блок кода, находящийся между фигурными скобками в языках вроде C или выделенный отступами в Python.

Одной из причин использования циклов do-while является их эффективность. Для примера, если условием цикла предусматриваются ресурсоёмкие операции и цикл должен быть выполнен n раз (n ≥ 1), то в цикле do-while условие будет запускаться n раз. При этом в обычном цикле while то же самое условие выполняется n + 1 раз.

В языке Python конструкция цикла do-while отсутствует. Почему так? По всей видимости, разработчики языка не смогли найти хорошее в плане синтаксиса решение для циклов этого типа. Вероятно, по этой причине Гвидо ван Россум отклонил проект PEP 315, в котором предпринималась попытка добавить в язык циклы do-while. Некоторые из разработчиков языка по-прежнему хотят добавить do-while и надеются оживить дискуссию по этой теме.

А пока вы можете изучить альтернативные варианты, доступные в Python. Итак, каким образом можно эмулировать цикл do-while в Python? Из этого руководства вы узнаете, как с помощью while создавать циклы, которые ведут себя аналогично циклам do-while.

Перейдём к делу: Использование цикла while и оператора break

Наиболее распространённый способ эмуляции цикла do-while в Python – это использование бесконечного цикла while с оператором break внутри конструкции if, которая проверяет заданное условие и прерывает цикл, если условие становится истинным:

while True:
    # Выполняется какое-то действие...
    # Обновление условия...
    if condition:
        break

Здесь в качестве формального условия используется True. Благодаря этому цикл становится бесконечным. Он выполняет все необходимые действия до условного оператора, после чего обновляет условие прерывания. Если условие выдаёт значение True, то оператор break осуществляет выход из цикла, и выполнение программы продолжается по обычному пути.

Примечание: Использование бесконечных циклов и операторов break позволяет эмулировать циклы do-while. Этот вариант обычно рекомендуется сообществом Python, но он не является полностью безопасным.

Например, если перед оператором break поместить оператор continue, то условие прерывания может быть пропущено и вы получите неконтролируемый бесконечный цикл.

Вот так на языке Python выглядит код, эквивалентный программе на C, которую вы видели во введении к этому руководству:

>>> while True:
...    number = int(input("Введите любое положительное число: "))
...    print(number)
...    if not number > 0:
...        break
...
Введите любое положительное число: 1
1
Введите любое положительное число: 4
4
Введите любое положительное число: -1
-1

Цикл получает входные данные пользователя с помощью встроенной функции input(). Затем с помощью функции int() эти данные конвертируются в целое число. Если пользователь введёт число, равное нулю или меньше, выполняется оператор break, и цикл завершается.

В некоторых ситуациях вам будет нужна гарантия того, что цикл выполнится как минимум один раз. В этих случаях можно использовать while и break, как указано выше. В следующем разделе мы напишем игру “Угадай число”, где используется такой цикл do-while для приёма и обработки данных, введённых пользователем в командной строке.

Как работают циклы do-while на практике?

Наиболее популярным вариантом использования циклов do-while является приём и обработка введённых пользователем данных. В качестве практического примера давайте возьмём игру “Угадай число”, реализованную на языке JavaScript. В её коде цикл do … while используется для обработки ввода пользователя:

const LOW = 1;
const HIGH = 10;

let secretNumber = Math.floor(Math.random() * HIGH) + LOW;
let clue = '';
let number = null;

do {
  let guess = prompt(`Угадайте число от ${LOW} до ${HIGH} ${clue}`);
  number = parseInt(guess);
  if (number > secretNumber) {
    clue = `(меньше чем ${number})`;
  } else if (number < secretNumber) {
    clue = `(больше чем ${number})`;
  }
} while (number != secretNumber);

alert(`Вы угадали! Загаданное число – это ${number}`);

Данный скрипт выполняет несколько задач. Вот краткий разбор того, что в нём происходит:

  • В строках 3 и 4 определяются две константы для обозначения интервала, в котором будет храниться загаданное число.
  • В строках 6-8 находятся переменные для хранения загаданного числа, сообщения-подсказки и начальное значение для переменной number, которая будет содержать введённые пользователем данные.
  • В строке 10 начинается цикл do …while, предназначенный для обработки введённых пользователем данных и определения того, угадал ли пользователь загаданное число.
  • В 11-й строке определена локальная переменная guess, необходимая для хранения пользовательских данных по мере их ввода в терминале.
  • В строке 12 введённое значение конвертируется в целое число с помощью функции parseInt().
  • В строке 13 размещён условный оператор, который смотрит, не больше ли введённое число, чем загаданное. Если условие совпадает, то в значение переменной clue устанавливается соответствующее сообщение.
  • В строке 15 проверяется, не меньше ли введённое число чем загаданное, а затем соответствующим образом изменяется переменная clue.
  • Строка 18 определяет условие цикла, который проверяет, отличается ли ввод от загаданного числа. В этом примере цикл будет продолжаться до тех пор, пока пользователь не введёт нужное число.
  • Наконец, в строке 20 активируется всплывающее окно, которое информирует пользователя о том, что он угадал.

Теперь предположим, что вы хотите перевести приведённый выше пример в код на языке Python. Эквивалентная игра “Угадай число” на Python будет выглядеть примерно так:

from random import randint

LOW, HIGH = 1, 10

secret_number = randint(LOW, HIGH)
clue = ""

while True:
    guess = input(f"Угадайте число от {LOW} до {HIGH} {clue} ")
    number = int(guess)
    if number > secret_number:
        clue = f"(меньше чем {number})"
    elif number < secret_number:
        clue = f"(больше чем {number})"
    else:
        break

print(f"Вы угадали! Загаданное число – это {number}")

Данный код на языке Python работает точно так же, как и его эквивалент на JavaScript. Основное отличие заключается в том, что в этом случае вам придётся использовать обычный цикл while, так как циклов do … while в питоне нет. В данной реализации, когда пользователь угадывает загаданное число, выполняется код после оператора else, прерывающий цикл. Последняя строка кода выводит сообщение о том, что число угадано.

Совмещение бесконечного цикла и оператора break, как в приведённом выше примере, является наиболее часто используемым вариантом эмуляции do-while в Python.

Чем цикл do-while отличается от while?

Если в двух словах, то основное различие между циклами do-while и циклами while заключается в том, что у первого тело выполняется по крайней мере один раз, так как условие цикла проверяется в конце. С другой стороны, тело обычного цикла while выполняется только в том случае, если условие выдаёт True, а проверка происходит в начале цикла.

В этой таблице мы суммируем ключевые различия между двумя типами циклов:

while do-while
Цикл контролируется на входе Цикл контролируется на выходе
Выполняется только в случае, если условие цикла истинно Выполняется до тех пор, пока условие цикла не станет ложным
Сначала происходит проверка условия, а затем выполняется тело цикла Выполняется тело цикла, а затем происходит проверка условия
Если условие цикла изначально ложно, тело цикла не выполняется ни разу Тело цикла выполняется по крайней мере один раз, независимо от того, истинное или ложное значение возвращает условие
Условие цикла проверяется n + 1 раз на n итераций Условие цикла проверяется n раз (n – количество итераций)

Цикл while – структура потока управления, которая отличается универсальностью. Она позволяет перезапускать набор инструкций, пока заданное условие остаётся истинным. У цикла do-while сценарии использования более специфичны. В основном он применяется там, где проверка состояния цикла имеет смысл только после как минимум однократного срабатывания тела цикла.

Какие альтернативы можно использовать для эмуляции циклов do-while в Python?

К этому моменту вы уже знаете рекомендуемый или наиболее популярный способ эмуляции цикла do-while в Python. Однако Python отличается достаточно большой гибкостью, в том числе по части решения этой задачи. Некоторые программисты постоянно пользуются бесконечным циклом while и оператором break. Другие программисты выбирают более оригинальные формулы.

В этом разделе мы рассмотрим некоторые альтернативные методы эмуляции цикла do-while. В первом варианте первая итерация происходит до начала цикла. Вторая альтернатива подразумевает использование условия, для которого до начала цикла задаётся значение True.

Выполнение первой итерации до начала цикла

Как вы уже знаете, самой важной особенностью do-while является то, что тело такого цикла выполняется как минимум один раз. Чтобы эмулировать эту особенность с помощью while, вы можете взять тело цикла и пройти по нему до начала цикла. Затем вам нужно будет повторить код внутри цикла.

Это решение кажется довольно скучным, и так оно и будет, если не использовать одну хитрость. К счастью, мы можем использовать функцию для упаковки тела цикла и предотвращения повторений. При использовании этого метода ваш код будет выглядеть примерно так:

condition = do_something()

while condition:
  condition = do_something()

Первый вызов функции do_something() гарантирует, что нужный код будет запущен минимум один раз. Вызов функции do_something() внутри цикла выполняется только в случае, если условие истинно. Обратите внимание: чтобы этот паттерн работал правильно, вам необходимо обновлять условие цикла на каждой итерации.

Приведённый ниже код показывает, как можно реализовать игру “Угадай число”, используя эту методику:

# guess.py

from random import randint

LOW, HIGH = 1, 10

secret_number = randint(LOW, HIGH)
clue = ""

def process_move(clue):
    user_input = input(f"Угадайте число от {LOW} до {HIGH} {clue} ")
    number = int(user_input)
    if number > secret_number:
        clue = f"(меньше чем {number})"
    elif number < secret_number:
        clue = f"(больше чем {number})"
    return number, clue

number, clue = process_move(clue)  # Первый проход

while number != secret_number:
    number, clue = process_move(clue)

print(f"Вы угадали! Загаданное число – это {number}")

В новой версии игры вы упаковываете всю функциональность цикла в функцию process_move(). Эта функция возвращает число, которое программа будет использовать позже при проверке условия цикла. Кроме того, она возвращает сообщение с подсказкой.

Обратите внимание, что функция process_move() один раз выполняется перед началом цикла, имитируя основную особенность цикла do-while (содержащийся в ней код выполняется как минимум один раз).

Внутри цикла функция запускает основную функциональность игры и соответствующим образом обновляет условие цикла.

Использование условия цикла с True в начале

Использование условия цикла, которому изначально присвоено значение True, является ещё одним вариантом эмуляции цикла do-while. В этом случае вам просто нужно присвоить условию значение True непосредственно перед началом выполнения цикла. Благодаря этому тело цикла будет выполнено как минимум один раз:

do = True

while do:
  do_something()
  if condition:
    do = False

Эта альтернативная конструкция очень похожа на ту, что мы рассматривали в предыдущем разделе. Основное отличие состоит в том, что условие цикла здесь представляет собой логическую переменную, которая обновляется внутри цикла.

Кроме того, данная методика похожа на ту, в которой используется бесконечный цикл while и оператор break. Однако этот вариант является более понятным и читаемым, поскольку в нём можно использовать описательные имена переменных вместо простого оператора break и “захардкоженного” условия True.

Примечание: Иногда более естественно выглядит такое название логической переменной, в котором для выхода из цикла нужно установить значение True. В этом случае цикл можно начать с выражения вроде while not done: а потом установить done == True внутри цикла.

В следующем примере показано, как с использованием этого метода переписать игру “Угадай число”:

# guess.py

from random import randint

LOW, HIGH = 1, 10

secret_number = randint(LOW, HIGH)
clue = ""
number_guessed = False

while not number_guessed:
    user_input = input(f"Угадайте число от {LOW} до {HIGH} {clue} ")
    number = int(user_input)
    if number > secret_number:
        clue = f"(меньше чем {number})"
    elif number < secret_number:
        clue = f"(больше чем {number})"
    else:
        number_guessed = True

print(f"Вы угадали! Загаданное число – это {number}")

В этом примере вы первым делом определяете логическую переменную number_guessed, которая помогает управлять циклом. Внутри цикла, как обычно, обрабатываются введённые пользователем данные. Если пользователь угадывает загаданное число, то переменной number_guessed присваивается значение True, и программа выходит из цикла выполнения.

Заключение

В этом уроке вы научились эмулировать цикл do-while на языке Python. В этом языке нет такой конструкции, хотя её можно найти в других языках, таких как C, C++, Java и JavaScript. Вы узнали, что аналог do-while несложно написать с помощью обычного цикла while, при этом существует несколько различных способов его создания.

Наиболее популярным методом эмуляции do-while является использование бесконечного цикла while с условным оператором в конце тела цикла. Этот условный оператор управляет циклом и осуществляет выход из него с помощью оператора break.

Вы также научились использовать несколько альтернативных методов создания кода, аналогичного циклу do-while. В число вариантов входит выполнение первой итерации перед циклом или использование для управления циклом логической переменной, которой изначально присвоено значение True.

Вооружившись этими знаниями, вы без труда сможете начать эмулировать циклы do-while в своём коде на Python.

https://realpython.com/python-do-while/

Written on December 24, 2022