Возможные дубликаты:
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
выполняет задачи, а затем проверяет условие выхода.
Ответы:
Если вы всегда хотите, чтобы цикл выполнялся хотя бы один раз. Это нечасто, но время от времени я им пользуюсь. Один из случаев, когда вы можете захотеть его использовать, - это попытка доступа к ресурсу, который может потребовать повторной попытки, например
do { try to access resource... put up message box with retry option } while (user says retry);
источник
do-while лучше, если компилятор не умеет оптимизировать. do-while имеет только один условный переход, в отличие от for и while, которые имеют условный переход и безусловный переход. Для процессоров, которые являются конвейерными и не прогнозируют переходы, это может иметь большое значение в производительности жесткого цикла.
Кроме того, поскольку большинство компиляторов достаточно умны, чтобы выполнить эту оптимизацию, все циклы, обнаруженные в декомпилированном коде, обычно будут выполняться в режиме выполнения (если декомпилятор вообще потрудится восстановить циклы из обратных локальных переходов).
источник
Я использовал это в функции TryDeleteDirectory. Это было что-то вроде этого
do { try { DisableReadOnly(directory); directory.Delete(true); } catch (Exception) { retryDeleteDirectoryCount++; } } while (Directory.Exists(fullPath) && retryDeleteDirectoryCount < 4);
источник
while
?Делать в то время как полезно, когда вы хотите выполнить что-то хотя бы один раз. Что касается хорошего примера использования do while vs. while, допустим, вы хотите сделать следующее: Калькулятор.
Вы можете подойти к этому, используя цикл и проверяя после каждого вычисления, хочет ли человек выйти из программы. Теперь вы, вероятно, можете предположить, что после открытия программы человек захочет сделать это хотя бы один раз, чтобы вы могли сделать следующее:
do { //do calculator logic here //prompt user for continue here } while(cont==true);//cont is short for continue
источник
continue
является ключевым словом; D== true
не нужен.cont == true
если cont истинно, возвращает истину. Это совершенно бесполезно.Это своего рода косвенный ответ, но этот вопрос заставил меня задуматься о его логике, и я подумал, что этим стоит поделиться.
Как уже говорили все, вы используете
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
версия всегда что-то добавляет.read()
никогда не должен вернутьсяNone
. Если вы используете библиотеку там, где она есть, предпосылка неприменима (т. Е. Что контрольное значение не входит в набор возможных контрольных значений).''
вместоNone
. Какое-то значение, которое оценивается как ложное. Списывайте это на мое незнаниеread
функции Python .read()
возврате''
к строке ничего не будет добавлено, она пуста.Я довольно часто их использовал, когда учился в школе, но не так часто с тех пор.
Теоретически они полезны, когда вы хотите, чтобы тело цикла выполнялось один раз перед проверкой условия выхода. Проблема в том, что для тех немногих случаев, когда я не хочу сначала проверять, обычно я хочу, чтобы проверка выхода находилась в середине тела цикла, а не в самом конце. В этом случае, я предпочитаю использовать хорошо известный
for (;;)
сif (condition) exit;
где - то в теле.Фактически, если меня немного не устраивает условие выхода из цикла, иногда я считаю полезным начать писать цикл как
for (;;) {}
с оператором выхода там, где это необходимо, а затем, когда я закончу, я могу посмотреть, может ли это быть " очищено »путем помещения инициализаций, условий выхода и / или увеличения кода вfor
круглые скобки.источник
for(;;)
к «бесконечному циклу», в то время какwhile(true)
я должен остановиться и подумать. Возможно, это потому, что я кодировал C до того, как появилсяtrue
. Раньше у нас был толькоTRUE
макрос, и если что-то было с ошибками, мне пришлось бы убедить себя, что макрос не был0
каким-то образом переопределен (это случилось!). С участиемfor(;;)
нет никаких шансов , что. Если вы настаиваете на форме while, я бы предпочел увидетьwhile (1)
. Конечно, тогда некоторые могут спросить, не буква ли это l ...codeify
текст, вы можете просто ввести его непосредственно в поле комментария. Я видел, как некоторые люди вставляли гиперссылки, но для меня это слишком сложно.Ситуация, когда вам всегда нужно запускать кусок кода один раз, а в зависимости от его результата, возможно, несколько раз. То же самое можно сделать и с обычной
while
петлей.rc = get_something(); while (rc == wrong_stuff) { rc = get_something(); } do { rc = get_something(); } while (rc == wrong_stuff);
источник
do while
если вы хотите запустить блок кода хотя бы один раз.while
с другой стороны, не всегда будет выполняться в зависимости от указанных критериев.источник
Это так просто:
предусловие против постусловия
Теперь, когда вы знаете секрет ... используйте их с умом :)
источник
Я вижу, что на этот вопрос дан адекватный ответ, но я хотел бы добавить этот очень конкретный сценарий использования. Вы можете начать использовать do ... while чаще.
do { ... } while (0)
часто используется для многострочных #defines. Например:
#define compute_values \ area = pi * r * r; \ volume = area * h
Это хорошо работает для:
-но- есть подводный камень для:
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);
источник
Ответы на данный момент обобщают общее использование do-while. Но OP попросил пример, так что вот один: получить ввод пользователя. Но ввод пользователя может быть недействительным - поэтому вы запрашиваете ввод, проверяете его, продолжаете, если он действителен, в противном случае повторяете.
С помощью do-while вы получаете ввод, пока ввод недействителен. В обычном цикле while вы получаете ввод один раз, но если он недействителен, вы получаете его снова и снова, пока он не станет действительным. Нетрудно заметить, что первый будет короче, элегантнее и проще в обслуживании, если тело цикла станет более сложным.
источник
break
если ввод правильный.Я использовал его для читателя, который читает одну и ту же структуру несколько раз.
using(IDataReader reader = connection.ExecuteReader()) { do { while(reader.Read()) { //Read record } } while(reader.NextResult()); }
источник
Я использовал 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)
источник
Вот моя теория, почему большинство людей (включая меня) предпочитают циклы 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 - это как специализированные отвертки в ящике для инструментов, которые вы никогда не используете, потому что стандартная отвертка делает все, что вам нужно.
источник
Я занимаюсь программированием около 12 лет, и всего 3 месяца назад я столкнулся с ситуацией, когда было действительно удобно использовать do-while, поскольку перед проверкой условия всегда требовалась одна итерация. Так что угадайте, ваше большое время впереди :).
источник
do
while
было бы лучшее решение без сожалений?Я не могу представить, как вы продержались так долго без
do...while
петли.Прямо сейчас есть один на другом мониторе, и в этой программе есть несколько таких циклов. Все они имеют форму:
do { GetProspectiveResult(); } while (!ProspectIsGood());
источник
Это довольно распространенная структура на сервере / потребителе:
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) или потоках-смотрителях в сравнении с пулами потоков - это просто призвано дать представление о конкретном варианте использования структура потока управления.
источник
Это мое личное мнение, но этот вопрос требует ответа, основанного на опыте:
Я программировал на C 38 лет и никогда не использую
do
/while
loops в обычном коде.Единственное убедительное использование этой конструкции - это макросы, в которых она может объединять несколько операторов в один оператор с помощью
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
определяется как achar
иwhile (c!='\n') c=getc(stream);
Вывод: избегайте
do
/while
зацикливайтесь и ищите ошибки, когда вы их видите.источник
while() {}
циклов, потому что они более безопасны с точки зрения начальных проверок, поэтому они заставляют вас "отключиться", лениться думать о лучшем алгоритме, проводить массу ненужных проверок ошибок и т. Д. "Отсутствиеdo{}while()
использования" - это признак плохого (безмозглого) программиста-новичка, производить менее качественный и медленный код. Поэтому я сначала спрашиваю себя: "Это строго случай while () {}?" Если нет - я просто попробую написать цикл do while, хотя это может потребовать больше размышлений и точности вводимых данныхbreak
илиreturn
заявления. Превращение циклаdo
/while
вfor(;;)
цикл будет более последовательным ИМХО. Мой остракизм по отношению к этому утверждению аналогичен моему отказу от его использованияstrncpy()
даже в тех редких случаях, когда оно было бы правильным инструментом: не позволяйте случайным читателям представить, что эти конструкции безвредны, потому что в большинстве случаев они являются источниками труднодоступных ошибок. .while
циклы проверяют условие перед циклом,do...while
циклы проверяют условие после цикла. Это полезно, если вы хотите основывать условие на побочных эффектах от выполнения цикла или, как говорят другие плакаты, если вы хотите, чтобы цикл запускался хотя бы один раз.Я понимаю, откуда вы, но
do-while
это то, что очень редко использую, и я никогда не использовал себя. Вы не ошиблись.Вы не ошиблись. Это все равно, что сказать, что кто-то делает что-то неправильно, потому что никогда не использовал
byte
примитив. Это просто не так часто используется.источник
Наиболее распространенный сценарий, с которым я сталкиваюсь, когда я использую цикл
do
/,while
- это небольшая консольная программа, которая запускается на основе некоторого ввода и повторяется столько раз, сколько хочет пользователь. Очевидно, что консольная программа не имеет смысла запускаться постоянно; но помимо первого раза это зависит от пользователя - следовательно,do
/,while
а не простоwhile
.Это позволяет пользователю при желании опробовать несколько различных входов.
do { int input = GetInt("Enter any integer"); // Do something with input. } while (GetBool("Go again?"));
Я подозреваю, что разработчики программного обеспечения используют
do
/ всеwhile
меньше и меньше в наши дни, теперь, когда практически каждая программа под солнцем имеет какой-то графический интерфейс. Это имеет больше смысла с консольными приложениями, так как необходимо постоянно обновлять вывод, чтобы предоставлять инструкции или предлагать пользователю новую информацию. Напротив, с графическим интерфейсом пользователя текст, предоставляющий эту информацию пользователю, может просто находиться в форме и никогда не должен повторяться программно.источник
Я все время использую циклы 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 ...Будучи чудаковатым программистом, во многих моих школьных проектах по программированию использовалось взаимодействие с текстовым меню. Практически все использовали что-то вроде следующей логики для основной процедуры:
do display options get choice perform action appropriate to choice while choice is something other than exit
Еще со школьных времен я обнаружил, что чаще использую цикл while.
источник
Одно из приложений, которые я видел, - это Oracle, когда мы смотрим на наборы результатов.
Как только у вас есть набор результатов, вы сначала извлекаете из него (делайте) и с этого момента ... проверяете, возвращает ли выборка элемент или нет (пока элемент найден ...). То же самое может быть применимо для любого другого " fetch-подобные "реализации.
источник
Я использовал его в функции, которая возвращала следующую позицию символа в строке 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
.Любой вид консольного ввода хорошо работает с do-while, потому что вы запрашиваете в первый раз и повторно запрашиваете, когда проверка ввода не выполняется.
источник
Несмотря на то, что есть много ответов, это мое мнение. Все сводится к оптимизации. Я покажу два примера, в которых один быстрее другого.
Случай 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, вполне возможно, что они столкнутся с генерацией идентификатора, где некоторые идентификаторы уже были взяты. Так что все сводится к большим проектам и оптимизации.
источник
Мне нравится понимать эти два слова как:
while
-> «повторять до»,do ... while
-> «повторять, если».источник
Я наткнулся на это, исследуя подходящий цикл для использования в моей ситуации. Я считаю, что это полностью удовлетворит общую ситуацию, когда цикл 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;
источник
Read()
и должен быть циклом while, поскольку первыйRead()
вызов обращается к первой строке. Внешний вызываетNextResult()
и должен быть временным, потому что первый набор данных результата активен до первогоNextResult()
вызова. Фрагменты кода не имеют особого смысла, особенно потому, что комментарии не соответствуют коду и являются неправильными.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;
источник