'do... while' vs. 'while'

Возможные дубликаты:
Пока vs. Do While
Когда следует использовать do-while вместо циклов?

Я программировал какое-то время (2 года работы + 4,5 года в год + 1 год до колледжа), и я никогда не использовал цикл do-while, который не был вынужден на курсе "Введение в программирование", У меня растущее чувство, что я неправильно программирую, если я никогда не сталкиваюсь с чем-то таким фундаментальным.

Может быть, я просто не столкнулся с правильными обстоятельствами?

Каковы некоторые примеры, когда нужно было бы использовать do-while вместо некоторого времени?

(Мое обучение было почти все на C/С++, и моя работа находится на С#, поэтому, если есть другой язык, где это абсолютно имеет смысл, потому что do-whiles работают по-другому, тогда эти вопросы действительно не применяются.)

Чтобы уточнить... Я знаю разницу между while и a do-while. Проверяет состояние выхода и выполняет задачи. do-while выполняет задачи, а затем проверяет условие выхода.

Ответ 1

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

do
{
   try to access resource...
   put up message box with retry option

} while (user says retry);

Ответ 2

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

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

Ответ 3

Сделайте то, что полезно, когда вы хотите что-то выполнить хотя бы один раз. Что касается хорошего примера использования do while vs. while, скажем, вы хотите сделать следующее: калькулятор.

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

do
{
    //do calculator logic here
    //prompt user for continue here
} while(cont==true);//cont is short for continue

Ответ 4

Я использовал это в функции TryDeleteDirectory. Это было что-то вроде этого

do
{
    try
    {
        DisableReadOnly(directory);
        directory.Delete(true);
    }
    catch (Exception)
    {
        retryDeleteDirectoryCount++;
    }
} while (Directory.Exists(fullPath) && retryDeleteDirectoryCount < 4);

Ответ 5

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

Как и все остальные, вы используете цикл do ... while, если хотите выполнить тело хотя бы один раз. Но при каких обстоятельствах вы хотите это сделать?

Ну, самый очевидный класс ситуаций, о котором я могу думать, будет тогда, когда первоначальное ( "непечатное" ) значение условия проверки будет таким же, как когда вы хотите выйти. Это означает, что вам нужно выполнить тело цикла один раз, чтобы перенести условие на не выходящее значение, а затем выполнить фактическое повторение на основе этого условия. Что, когда программисты были ленивы, кто-то решил обернуть это в структуру управления.

Итак, например, чтение символов из последовательного порта с таймаутом может иметь форму (в Python):

response_buffer = []
char_read = port.read(1)

while char_read:
    response_buffer.append(char_read)
    char_read = port.read(1)

# When there nothing to read after 1s, there is no more data

response = ''.join(response_buffer)

Обратите внимание на дублирование кода: char_read = port.read(1). Если у Python был цикл do ... while, я мог бы использовать:

do:
    char_read = port.read(1)
    response_buffer.append(char_read)
while char_read

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

response_buffer = []
char_read = None

while char_read != '':
    char_read = port.read(1)
    response_buffer.append(char_read)

response = ''.join(response_buffer)

Итак, вот суть моей точки: в языках с нулевыми типами ситуация initial_value == exit_value возникает гораздо реже, а , что может быть, почему вы ее не встретили. Я не говорю, что этого не происходит, потому что еще есть моменты, когда функция возвращает None, чтобы обозначить действительное условие. Но в моем поспешном и кратко продуманном мнении это произошло бы намного больше, если бы языки, которые вы использовали, не учитывали значение, которое означает: эта переменная еще не была инициализирована.

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

Ответ 6

Я использовал их честно, когда я учился в школе, но не так много.

В теории они полезны, когда вы хотите, чтобы тело цикла выполнялось один раз перед проверкой состояния выхода. Проблема в том, что для нескольких экземпляров, где я не хочу сначала проверять, обычно я хочу проверить выход в середине тела цикла, а не в самом конце. В этом случае я предпочитаю использовать известный for (;;) с if (condition) exit; где-то в теле.

В самом деле, если я немного шаткий в условии выхода цикла, иногда мне полезно начать писать цикл как for (;;) {} с инструкцией выхода, где это необходимо, а затем, когда я закончил, я могу посмотрите, можно ли его "очистить" путем перемещения инициализаций, условий выхода и/или кода увеличения в круглых скобках for.

Ответ 7

do while - это если вы хотите хотя бы один раз запустить блок кода. while, с другой стороны, не всегда будет выполняться в зависимости от указанных критериев.

Ответ 8

Ситуация, когда вам всегда нужно запускать кусок кода один раз и в зависимости от его результата, возможно, больше времени. То же самое можно создать с помощью регулярного цикла while.

rc = get_something();
while (rc == wrong_stuff)
{
    rc = get_something();
}

do
{
    rc = get_something();
}
while (rc == wrong_stuff);

Ответ 9

Это так просто:

предварительное условие и постусловие

  • while (cond) {...} - precondition, он выполняет код только после проверки.
  • do {...} while (cond) - постусловие, код выполняется хотя бы один раз.

Теперь, когда вы знаете секрет, используйте их с умом:)

Ответ 10

Я вижу, что этот вопрос получил адекватный ответ, но хотел бы добавить этот очень специфический сценарий использования. Вы можете начать использовать do... еще чаще.

do
{
   ...
} while (0)

часто используется для многострочных #defines. Например:

#define compute_values     \
   area = pi * r * r;      \
   volume = area * h

Это работает нормально:

r = 4;
h = 3;
compute_values;

-but- есть gotcha для:

if (shape == circle)  compute_values;

поскольку это расширяется до:

if (shape == circle) area = pi *r * r;
volume = area * h;

Если вы завернете его в цикл do... while (0), он правильно расширится до одного блока:

if (shape == circle)
  do
  {
    area = pi * r * r;
    volume = area * h;
  } while (0);

Ответ 11

Ответы до сих пор обобщают общее использование для do-while. Но OP попросил пример, так вот вот один: получить пользовательский ввод. Но пользовательский ввод может быть недействительным, поэтому вы запрашиваете ввод, проверяете его, действуете, если он действителен, в противном случае повторяется.

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

Ответ 12

Я программирую около 12 лет, и только 3 месяца назад я встретил ситуацию, когда было действительно удобно использовать do-while, поскольку перед проверкой состояния всегда требовалась одна итерация. Так что догадайтесь, что ваше большое время впереди:).

Ответ 13

Я использовал его для чтения, который читает одну и ту же структуру несколько раз.

using(IDataReader reader = connection.ExecuteReader())
{
    do
    {
        while(reader.Read())
        {
            //Read record
        }
    } while(reader.NextResult());
}

Ответ 14

Здесь моя теория, почему большинство людей (включая меня) предпочитают while() {} петли делать {} while(): цикл while() {} можно легко адаптировать для выполнения как цикл do..while() в то время как противоположное неверно. Цикл while определен определенным образом "более общий". Также программисты любят легко улавливать шаблоны. В цикле while прямо начинается то, что его инвариант, и это хорошая вещь.

Вот что я имею в виду о "более общем". Возьмите этот цикл do..while:

do {
 A;
 if (condition) INV=false; 
 B;
} while(INV);

Преобразование этого в цикл while является простым:

INV=true;
while(INV) {
 A;
 if (condition) INV=false; 
 B;
}

Теперь возьмем модель while while:

while(INV) {
     A;
     if (condition) INV=false; 
     B;
}

И преобразуйте это в цикл do..while, получив это чудовище:

if (INV) {
 do
 {
         A;
         if (condition) INV=false; 
         B;

 } while(INV)
}

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

Ответ 15

Я не могу себе представить, как вы прошли это долго, не используя цикл do...while.

Там один на другой монитор прямо сейчас, и в этой программе есть несколько таких циклов. Все они имеют форму:

do
{
    GetProspectiveResult();
}
while (!ProspectIsGood());

Ответ 16

Циклы

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

Я понимаю, откуда вы пришли, но do-while - это то, что чаще всего используется редко, и я никогда не использовал себя. Вы не ошибаетесь.

Ты не ошибаешься. Это похоже на то, что кто-то делает это неправильно, потому что они никогда не использовали примитив byte. Это просто не так часто используется.

Ответ 17

Я использовал do while, когда я читаю значение часового в начале файла, но, кроме этого, я не считаю ненормальным, что эта структура не слишком часто используется - do-while действительно ситуативны.

-- file --
5
Joe
Bob
Jake
Sarah
Sue

-- code --
int MAX;
int count = 0;
do {
MAX = a.readLine();
k[count] = a.readLine();
count++;
} while(count <= MAX)

Ответ 18

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

Это позволяет пользователю при желании опробовать множество разных входов.

do
{
   int input = GetInt("Enter any integer");
   // Do something with input.
}
while (GetBool("Go again?"));

Я подозреваю, что разработчики программного обеспечения используют do/while все меньше и меньше в наши дни, теперь, когда практически каждая программа под солнцем имеет какой-то графический интерфейс. Это имеет больше смысла в консольных приложениях, так как необходимо постоянно обновлять выходные данные для предоставления инструкций или запрашивать у пользователя новую информацию. С графическим интерфейсом, напротив, текст, предоставляющий информацию пользователю, может просто сидеть на форме и никогда не должен повторяться программно.

Ответ 19

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

# some comments
# some more comments
column1 column2
  1.234   5.678
  9.012   3.456
    ...     ...

Я буду использовать цикл do-while для чтения строки "column1 column2", чтобы я мог искать интересующий столбец. Здесь псевдокод:

do {
    line = read_line();
} while ( line[0] == '#');
/* parse line */

Затем я сделаю цикл while, чтобы прочитать оставшуюся часть файла.

Ответ 20

Будучи программистом-гейзером, многие из моих школьных программных проектов использовали взаимодействие с текстовым меню. Практически все использовали для основной процедуры что-то вроде следующей логики:

do
    display options
    get choice
    perform action appropriate to choice
while choice is something other than exit

Со школьных дней я обнаружил, что чаще использую цикл while.

Ответ 21

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

Как только у вас есть результирующий набор, вы сначала извлекаете из него (do) и с этой точки.. проверьте, возвращает ли выборка элемент или нет (в то время как элемент найден..). То же самое может быть применимо для любых других реализаций типа "выборка".

Ответ 22

Я использовал его в функции, которая вернула следующую позицию символа в строке utf-8:

char *next_utf8_character(const char *txt)
{
    if (!txt || *txt == '\0')
        return txt;

    do {
        txt++;
    } while (((signed char) *txt) < 0 && (((unsigned char) *txt) & 0xc0) == 0xc0)

    return (char *)txt;
}

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

Ответ 23

Любой вход в консоль хорошо работает с do-while, потому что вы запрашиваете первый раз и повторно запрашиваете всякий раз, когда не выполняется проверка ввода.

Ответ 24

Это довольно распространенная структура на сервере/потребителе:

DOWHILE (no shutdown requested)
   determine timeout
   wait for work(timeout)
   IF (there is work)
      REPEAT
          process
      UNTIL(wait for work(0 timeout) indicates no work)
      do what is supposed to be done at end of busy period.
   ENDIF
ENDDO

the REPEAT UNTIL(cond) является do {...} while(!cond)

Иногда ожидание работы (0) может быть более дешевым процессором (даже исключение расчета тайм-аута может быть улучшением с очень высокими скоростями прибытия). Более того, существует теория many теории массового обслуживания, которая делает число, которое служило в период занятости важной статистикой. (См. Например, Kleinrock - том 1.)

Аналогично:

DOWHILE (no shutdown requested)
   determine timeout
   wait for work(timeout)
   IF (there is work)
      set throttle
      REPEAT
          process
      UNTIL(--throttle<0 **OR** wait for work(0 timeout) indicates no work)
   ENDIF
   check for and do other (perhaps polled) work.
ENDDO

где check for and do other work может оказаться чрезмерно дорогостоящим для включения в основной цикл или, возможно, ядро, которое не поддерживает эффективную операцию типа waitany(waitcontrol*,n) или, возможно, ситуацию, когда приоритетная очередь может голодать на другую работу, а дроссель используется как контроль голодания.

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

Я действительно не хочу вступать в дискуссию о псевдокоде (например, нужно ли проверять завершение работы в UNTIL) или потоки смотрителя по сравнению с пулами потоков - это просто предназначено для того, чтобы придать аромат конкретного использование случая структуры потока управления.

Ответ 25

Это мое личное мнение, но этот вопрос требует ответа, основанного на опыте:

  • Я программировал на C в течение 36 лет, и я никогда не использовал циклы do/while в обычном коде.

  • Единственное убедительное использование этой конструкции - это макросы, где она может переносить несколько операторов в один оператор через do { multiple statements } while (0)

  • Я видел бесчисленные примеры циклов do/while с ложным обнаружением ошибок или вызовами избыточных функций.

  • Мое объяснение этому наблюдению заключается в том, что программисты склонны неправильно моделировать проблемы, когда они думают в терминах циклов do/while. Они либо пропускают важное конечное условие, либо пропускают возможный отказ начального условия, которое они двигают до конца.

По этим причинам я пришел к выводу, что там, где есть цикл do/while, есть ошибка, и я регулярно бросаю вызов программистам-новичкам, чтобы показать мне цикл do/while, где Я не могу обнаружить ошибку рядом.

Этот тип цикла можно легко избежать: используйте for (;;) { ... } и добавьте необходимые тесты завершения, где они подходят. Довольно часто бывает, что необходимо провести более одного такого теста.

Вот классический пример:

/* skip the line */
do {
    c = getc(fp);
} while (c != '\n');

Это не удастся, если файл не закончится новой строкой. Тривиальным примером такого файла является пустой файл.

Лучшая версия:

int c;  // another classic bug is to define c as char.
while ((c = getc(fp)) != EOF && c != '\n')
    continue;

В качестве альтернативы, эта версия также скрывает переменную c:

for (;;) {
    int c = getc(fp);
    if (c == EOF || c == '\n')
        break;
}

Попробуйте найти while (c != '\n'); в любой поисковой системе, и вы найдете такие ошибки (извлеченные 24 июня 2017 года):

В ftp://ftp.dante.de/tex-archive/biblio/tib/src/streams.c функция getword(stream,p,ignore) имеет do/while и, по крайней мере, 2 ошибки:

  • c определяется как char и
  • существует потенциальный бесконечный цикл while (c!='\n') c=getc(stream);

Заключение: избегайте циклов do/while и ищите ошибки, когда вы их видите.

Ответ 26

Я столкнулся с этим, исследуя правильный цикл для использования в ситуации, которая у меня есть. Я считаю, что это полностью удовлетворит общую ситуацию, когда do.. while цикл является лучшей реализацией, чем цикл while (язык С#, поскольку вы заявили, что это ваш основной для работы).

Я создаю список строк на основе результатов SQL-запроса. Возвращаемый объект по моему запросу является SQLDataReader. Этот объект имеет функцию Read(), которая продвигает объект к следующей строке данных и возвращает true, если была другая строка. Он вернет false, если нет другой строки.

Используя эту информацию, я хочу вернуть каждую строку в список, а затем остановится, когда больше нет данных для возврата. A Do... Хотя цикл работает лучше всего в этой ситуации, так как он гарантирует, что добавление элемента в список произойдет, прежде чем проверять, есть ли другая строка. Причина этого должна быть сделана ДО ТОГО, проверяя время (условие), что при проверке он также продвигается. Использование цикла while в этой ситуации приведет к обходу первой строки из-за характера этой конкретной функции.

Короче:

В моей ситуации это не сработает.

    //This will skip the first row because Read() returns true after advancing.
    while (_read.NextResult())
           {
               list.Add(_read.GetValue(0).ToString());
           }

   return list;

Это будет.

    //This will make sure the currently read row is added before advancing.
    do
        {
            list.Add(_read.GetValue(0).ToString());
        } 
        while (_read.NextResult());

    return list;

Ответ 27

Console.WriteLine("hoeveel keer moet je de zin schrijven?");
        int aantal = Convert.ToInt32(Console.ReadLine());
        int counter = 0;

        while ( counter <= aantal)
        {
            Console.WriteLine("Ik mag geen stiften gooien");
            counter = counter + 1;