«делать… пока» против «пока»


86

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

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

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

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

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

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


42
Как бы то ни было, после десятилетия программирования в качестве моей карьеры я использовал цикл do-while, например, один или два раза.
Мэтт Грир

@Matt - От этого мне становится легче. По крайней мере, это означает, что я не одинок.
mphair

2
Я поддержу Мэтта и добавлю к этому еще десять лет или около того. Я тоже редко использую цикл do-while.
quentin-starin

МНОГО дубликатов. Вот тот, который ссылается на кучу из них: stackoverflow.com/questions/3094972/…
gnovice

3
Правильный «главный вопрос» кажется stackoverflow.com/questions/224059/…
Donal Fellows

Ответы:


115

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

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

} while (user says retry);

3
Так что я предполагаю, что это последует под фразой «Я не сталкивался с этим обстоятельством».
mphair

7
честно говоря, я очень редко сталкиваюсь с обстоятельствами, когда мне просто нужно использовать do..time ..
Стэн Р.

1
Может быть, немного короче, но Боб прав. Do / while следует использовать, если вы хотите запустить блок кода хотя бы один раз . Вам следует использовать цикл while, если вы не знаете, хотите ли вы запустить его один раз. Тем не менее, я могу сказать вам, что с 15-летним опытом разработки я использовал во много раз больше циклов while, чем циклы do / while.
ConsultUtah

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

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

65

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

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


5
+1 для добавления соответствующей технической информации к выбору между формами петель.
quentin-starin

2
Не только преждевременно, но и бесполезно. Если вам действительно нужен цикл while, то есть возможность выполнять итерацию 0 раз, вы должны заключить цикл в if, который вернет этот переход.
JeremyP

3
@Jeremy: Скачок, о котором вы говорите, находится вне цикла, он не выполняется N раз.
Ben Voigt

1
Рассмотрим также классическое использование do-while в устройстве Даффа (в котором цикл while преобразуется в развернутый do-while с большим шагом плюс таблица переходов для обработки остатка), что резко снижает накладные расходы цикла - в таких приложениях, как memset, memcmp, memcpy он может увеличить пропускную способность в 10 раз.
Бен Фойгт,

@BenVoigt Я знаю, что вашему комментарию уже десять лет, но устройство Даффа не следует упоминать без очень важной оговорки - оно стало менее эффективным в современных компиляторах, потому что современные компиляторы обычно могут разумно развертывать простые циклы сами по себе, что оптимально для целевой машины , хотя у них обычно нет логики для определения предназначения устройства Даффа и, следовательно, они не могут его оптимизировать. Это отличный урок об оптимизации кода для некоторой целевой машины, а не для абстрактного «достаточно продвинутого оптимизатора».
mtraceur

12

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

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

Ницца. Это первый пример, который имеет смысл заключить в do ... while.
Джошуа

4
Почему / чем это лучше стандарта while?
Джош Стодола

Цикл всегда выполняется один раз, поэтому вы можете что-то сделать, а затем попробуйте еще раз, если он не удастся. Если вы используете обычный цикл while, вы будете проверять, не сработает ли он, еще до того, как сделаете это на первой итерации.
NibblyPig

1
@SLC Но разве вы не хотели бы заранее проверить, существует ли каталог?
Джош Стодола

1
@Josh В этом примере да. Но если вы пытаетесь открыть соединение с занятым сервером, вы сначала подключитесь и посмотрите, был ли ответ «занято». Если бы это было так, вы бы попробовали еще несколько раз, прежде чем сдаться.
NibblyPig

11

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

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

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

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

7
continueявляется ключевым словом; D
Thomas Eding

Атринитис: Лол ... Я поскользнулся. Спасибо, что поправили меня.

== trueне нужен. cont == trueесли cont истинно, возвращает истину. Это совершенно бесполезно.
Mr.DeleteMyMessages

11

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

Как уже говорили все, вы используете 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's 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не загрязняет пространство имен функции. Но обратите внимание, что есть лучший способ сделать это - использовать Noneзначение Python :

response_buffer = []
char_read = None

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

response = ''.join(response_buffer)

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

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


Это один из самых интересных ответов, которые я прочитал здесь. Хороший вклад.
Боб Мур

Эти две версии не эквивалентны. Если первое чтение возвращается None, whileверсия правильно ничего не добавляет в буфер ответа, но ваша doверсия всегда что-то добавляет.
Дэвид Стоун

@DavidStone read()никогда не должен вернуться None. Если вы используете библиотеку там, где она есть, предпосылка неприменима (т. Е. Что контрольное значение не входит в набор возможных контрольных значений).
Detly

С вашим последним редактированием я вижу, что я должен был сказать ''вместо None. Какое-то значение, которое оценивается как ложное. Списывайте это на мое незнание readфункции Python .
Дэвид Стоун

@DavidStone Я не понимаю. При read()возврате ''к строке ничего не будет добавлено, она пуста.
Detly

7

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

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

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


5
Из любопытства ... почему "for (;;)" вместо "while (true)"?
mphair

4
Наверное, просто попробуй. Я стал приравниваться for(;;)к «бесконечному циклу», в то время как while(true)я должен остановиться и подумать. Возможно, это потому, что я кодировал C до того, как появился true. Раньше у нас был только TRUEмакрос, и если что-то было с ошибками, мне пришлось бы убедить себя, что макрос не был 0каким-то образом переопределен (это случилось!). С участиемfor(;;) нет никаких шансов , что. Если вы настаиваете на форме while, я бы предпочел увидеть while (1). Конечно, тогда некоторые могут спросить, не буква ли это l ...
TED

1
@mphair: Да, если вы обратите внимание на текст, который редактор вставляет, когда вы выделяете курсивом или codeifyтекст, вы можете просто ввести его непосредственно в поле комментария. Я видел, как некоторые люди вставляли гиперссылки, но для меня это слишком сложно.
TED

2
@TED: Я предпочитаю обозначать цикл до явного выхода "do {} while (1);". Между прочим, во встроенных системах моя нормальная парадигма цикла: «i = 10; do {} while (- i);». Для плотных циклов это намного быстрее, чем обычно для (); Я считаю, что удобнее использовать это последовательно в качестве шаблона цикла, когда счетчик вверх не требуется, чем произвольно использовать for () в случаях, не критичных для производительности.
supercat

2
Добавляя к while (1) и для (;;) рассуждения, я видел код, использующий for (EVER) с EVER, определенным как ;;
asr

6

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

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

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

6

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


5

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

предусловие против постусловия

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

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


1
Как я уже сказал, я знаю, как они работают, я просто не был уверен, в каком случае одно имеет смысл перед другим.
mphair

Логично, что вы используете do {} while, когда точно знаете, что должны пройти хотя бы один раз через цикл. Например, если у вас есть функция, которая находит элемент в массиве, и вы ранее поместили массив if.IsEmpty () then return; . Если он прошел эту проверку, вы можете смело использовать do {} while. do-while также легко представить как повторяющийся цикл: «Я ем до тех пор, пока не буду удовлетворен», что означает do {eat ();} while (! удовлетворительно ();). Проверка условия перед циклом считается более безопасной .. и поэтому используется чаще.
Ioan Paul Pirau

4

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

do
{
   ...
} while (0)

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

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

Это хорошо работает для:

r = 4;
h = 3;
compute_values;

-но- есть подводный камень для:

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);

2

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

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


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

@mphair: На самом деле, для такого рода ситуаций я мог бы иногда быть более склонным использовать "do {} while (0);" цикл, используя "continue;" если ввод пользователя неприемлем. Если есть только одна проблема и сообщение, используйте "do {} while (1);" и "перерыв"; работает хорошо, но если есть несколько причин требовать повторного входа, «продолжить»; конструкция работает лучше.
supercat

2

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

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

1

Я использовал a, 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)

1

Вот моя теория, почему большинство людей (включая меня) предпочитают циклы 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(INV) {
     A;
     if (condition) INV=false; 
     B;
}

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

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

 } while(INV)
}

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


1
Замена "do {} while (x);" цикл в "while (x);" цикл требует, чтобы X было либо условием, которое можно легко «подготовить», либо чтобы оно было «естественно» истинным на первом проходе. На мой взгляд, инструкции инициализации перед циклом подразумевают, что что-то внутри цикла требует подготовки, а цикл while (x) {}; "подразумевает, что цикл может выполняться ноль раз. Если мне нужен цикл, который будет выполняться хотя бы один раз, я используйте цикл «do {} while (x);»
supercat

1

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


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

2
Если вы правы, то do-while будет исключен из всех языков программирования, чтобы не сбивать с толку программистов :).
Нарек

@Narek: Прошло почти семь лет с тех пор, как вы написали это, было ли у вас больше обстоятельств, в которых do whileбыло бы лучшее решение без сожалений?
chqrlie

Да, наверное, еще раз: D
Нарек

1

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

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

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

1

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

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

REPEAT UNTIL(cond)будучиdo {...} while(!cond)

Иногда ожидание работы (0) может быть дешевле с точки зрения ЦП (даже исключение вычисления тайм-аута может быть улучшением с очень высокой скоростью поступления). Более того, есть много результатов теории массового обслуживания, которые делают количество обслуживаемых в период занятости важной статистической информацией. (См., Например, Kleinrock - Vol 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) или потоках-смотрителях в сравнении с пулами потоков - это просто призвано дать представление о конкретном варианте использования структура потока управления.


1

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

  • Я программировал на C 38 лет и никогда не использую do/ whileloops в обычном коде.

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

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

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

По этим причинам я пришел к выводу, что тамdowhile , где есть цикл / , есть ошибка , и я регулярно предлагаю начинающим программистам показать мне цикл 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определяется как a charи
  • есть потенциальный бесконечный цикл while (c!='\n') c=getc(stream);

Вывод: избегайте do/ whileзацикливайтесь и ищите ошибки, когда вы их видите.


1
Я предпочитаю избегать while() {}циклов, потому что они более безопасны с точки зрения начальных проверок, поэтому они заставляют вас "отключиться", лениться думать о лучшем алгоритме, проводить массу ненужных проверок ошибок и т. Д. "Отсутствие do{}while()использования" - это признак плохого (безмозглого) программиста-новичка, производить менее качественный и медленный код. Поэтому я сначала спрашиваю себя: "Это строго случай while () {}?" Если нет - я просто попробую написать цикл do while, хотя это может потребовать больше размышлений и точности вводимых данных
xakepp35

Я понимаю ваш ответ, но я мог бы подумать о каком-то возможном сценарии, когда это было бы лучше. Пожалуйста, посмотрите while: prnt.sc/v2zy04 или do while: prnt.sc/v2zyka . Я не был программистом так долго, как вы, но я не нашел там ошибки. Совершенно ясно, что do while более чистое, потому что не требует дополнительной проверки while для входа в цикл. Если мы говорим об оптимизации кода, я бы сказал, что это довольно чисто.
Рой Беррис,

@RoyBerris: ваш пример интересен. В программе на C функции, используемые внутри цикла, потребуют дополнительных тестов, чтобы убедиться, что они не сработали. Это добавит дополнительный беспорядок и breakили returnзаявления. Превращение цикла do/ whileв for(;;)цикл будет более последовательным ИМХО. Мой остракизм по отношению к этому утверждению аналогичен моему отказу от его использования strncpy() даже в тех редких случаях, когда оно было бы правильным инструментом: не позволяйте случайным читателям представить, что эти конструкции безвредны, потому что в большинстве случаев они являются источниками труднодоступных ошибок. .
chqrlie

@chqrlie, видя, что исходный ответ касался любого языка C, я думаю, подойдут оба ответа. C # намного «спасает», чем их предшественники, поэтому технически он будет быстрее с do while.
Рой Беррис,

0

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

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

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


0

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

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

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

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


0

Я все время использую циклы 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, чтобы прочитать остальную часть файла.


Поддерживать комментарии в любом месте файла было бы тривиально (и упростить код в целом), если бы вы просто поместили if (line[0]=='#') continue;сразу после read_lineвызова в свой основной цикл while ...
R .. GitHub ОСТАНОВИТЬ ПОМОЩЬ ICE

Не понимаю, как бы я реализовал ваше предложение по поиску заголовка. (блин, не могу понять, как форматировать код в комментариях, пожалуйста, помогите!) header = false; в то время как (! заголовок) {строка = read_line (); если (строка [0] == '#') продолжить; заголовок = истина; } вам это понятнее / проще?
вылетает

0

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

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

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


0

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

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


0

Я использовал его в функции, которая возвращала следующую позицию символа в строке 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;
}

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


Это неприятный способ написания *(unsigned char *)txt-0x80U<0x40.
R .. GitHub НЕ ПОМОГАЕТ ICE

0

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


0

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

Случай 1: while

string fileName = string.Empty, fullPath = string.Empty;

while (string.IsNullOrEmpty(fileName) || File.Exists(fullPath))
{
    fileName = Guid.NewGuid().ToString() + fileExtension;
    fullPath = Path.Combine(uploadDirectory, fileName);
}

Случай 2: do while

string fileName = string.Empty, fullPath = string.Empty;

do
{
    fileName = Guid.NewGuid().ToString() + fileExtension;
    fullPath = Path.Combine(uploadDirectory, fileName);
}
while (File.Exists(fullPath));

Итак, двое будут делать одно и то же. Но есть одно фундаментальное различие, а именно то, что для while требуется дополнительный оператор для ввода while. Что некрасиво, потому что, скажем, все возможные сценарии Guidкласса уже приняты, за исключением одного варианта. Это означает, что мне придется перебирать 5,316,911,983,139,663,491,615,228,241,121,400,000раз. Каждый раз, когда я дохожу до конца своего оператора while, мне нужно будет выполнить string.IsNullOrEmpty(fileName)проверку. Таким образом, это займет немного, крошечную долю работы процессора. Но учитывает ли эта очень маленькая задача возможные комбинации, имеющиеся в Guidклассе, и мы говорим о часах, днях, месяцах или дополнительном времени?

Конечно, это крайний пример, потому что вы, вероятно, не увидите этого в производстве. Но если мы подумаем об алгоритме YouTube, вполне возможно, что они столкнутся с генерацией идентификатора, где некоторые идентификаторы уже были взяты. Так что все сводится к большим проектам и оптимизации.



-1

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

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

Используя эту информацию, я хочу вернуть каждую строку в список, а затем останавливаться, когда данных для возврата больше нет. В этой ситуации лучше всего работает цикл Do ... While, так как он гарантирует, что добавление элемента в список произойдет ДО проверки наличия другой строки. Причина, по которой это должно быть сделано ДО проверки while (условия), заключается в том, что при проверке оно также продвигается. Использование цикла 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;

Использование SQLDataReader будет вложенными циклами. Внутренний вызывает Read()и должен быть циклом while, поскольку первый Read()вызов обращается к первой строке. Внешний вызывает NextResult()и должен быть временным, потому что первый набор данных результата активен до первого NextResult()вызова. Фрагменты кода не имеют особого смысла, особенно потому, что комментарии не соответствуют коду и являются неправильными.
Бен Фойгт

-1
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;

Вы должны ответить на свой собственный вопрос, как если бы вы собирались ответить на чужой. Включено объяснение, а также код
Саймон
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.