делать...а' и 'А'
Возможные Дубликаты:
а и не А
когда я должен использовать do-while вместо циклов while?
Я уже некоторое время программирую (2 года работы + 4,5 года степени + 1 год до колледжа), и я никогда не использовал цикл do-while, не будучи вынужденным к введению в курс программирования. У меня растет чувство, что я делаю Программирование неправильно, если я никогда не сталкиваюсь с чем-то таким основоположный.
может быть, я просто не в правильное обстоятельства?
Каковы некоторые примеры, где было бы необходимо использовать do-while вместо while?
(мое обучение было почти все в C / C++ и моя работа в C#, так что если есть другой язык, где это абсолютно имеет смысл, потому что do-whiles работают по-разному, то эти вопросы на самом деле не применяются.)
уточнить...Я знаю разницу между while
и а do-while
. В то время как проверяет условие выхода, а затем выполняет задачи. do-while
выполняет задачи, а затем проверяет условие выхода.
27 ответов:
Если вы всегда хотите, чтобы цикл выполняется хотя бы один раз. Это не часто, но я использую его время от времени. Один из случаев, когда вы можете использовать его, пытается получить доступ к ресурсу, который может потребовать повторной попытки, например
do { try to access resource... put up message box with retry option } while (user says retry);
do-while лучше, если компилятор не компетентен в оптимизации. do-while имеет только один условный прыжок, в отличие от for и while, которые имеют условный прыжок и безусловный прыжок. Для процессоров, которые конвейеризованы и не выполняют предсказание ветвей, это может иметь большое значение в производительности жесткого цикла.
кроме того, поскольку большинство компиляторов достаточно умны, чтобы выполнить эту оптимизацию, все циклы, найденные в декомпилированном коде, обычно будут do-while (если декомпилятор даже беспокоится о том, чтобы восстановить петли из обратных локальных gotos вообще).
Do while полезно, когда вы хотите выполнить что-то хотя бы один раз. А для хорошего примера для использования сделать в то время и в то время как, допустим, вы хотите сделать следующее: калькулятор.
вы можете подойти к этому с помощью цикла и проверки после каждого вычисления, если человек хочет выйти из программы. Теперь вы, вероятно, можете предположить, что после открытия программы Человек хочет сделать это хотя бы один раз, чтобы вы могли сделать следующее:
do { //do calculator logic here //prompt user for continue here } while(cont==true);//cont is short for continue
я использовал это в функции TryDeleteDirectory. Это было что-то вроде этого
do { try { DisableReadOnly(directory); directory.Delete(true); } catch (Exception) { retryDeleteDirectoryCount++; } } while (Directory.Exists(fullPath) && retryDeleteDirectoryCount < 4);
это своего рода косвенный ответ, но этот вопрос заставил меня задуматься о логике, и я подумал, что стоит поделиться.
как все остальные сказали, вы используете
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
не загрязняет пространство имен функции. Но отметим также, что есть лучший способ сделать это, и это с помощью языка PythonNone
значение:response_buffer = [] char_read = None while char_read != '': char_read = port.read(1) response_buffer.append(char_read) response = ''.join(response_buffer)
так вот, суть моей точки зрения: в языках с типами nullable, ситуация
initial_value == exit_value
возникает гораздо реже, и это может быть, поэтому вы этого не делаете сталкиваться с ним. Я не говорю, что это никогда не происходит, потому что есть еще времена, когда функция будет возвращатьNone
для обозначения допустимого условия. Но по моему поспешному и кратко обдуманному мнению, это произошло бы намного больше, если бы языки, которые вы использовали, не допускали значения, которое означает: эта переменная еще не была инициализирована.это не идеальное рассуждение: в действительности, теперь, когда нулевые значения являются общими, они просто образуют еще один элемент набора допустимых значений a переменная может принимать. Но на практике у программистов есть способ отличить переменную, находящуюся в разумном состоянии, которое может включать в себя состояние выхода из цикла, и она находится в неинициализированном состоянии.
я использовал их довольно много, когда я был в школе, но не так много с тех пор.
теоретически они полезны, когда вы хотите, чтобы тело цикла выполнялось один раз перед проверкой условия выхода. Проблема в том, что в тех немногих случаях, когда я не хочу сначала проверять,обычно я хочу проверить выход в середине тела цикла, а не в самом конце. В таком случае, я предпочитаю использовать хорошо известный
for (;;)
сif (condition) exit;
где-то в теле.In дело в том, что если я немного шатаюсь по условию выхода из цикла, иногда мне полезно начать писать цикл как
for (;;) {}
с оператором exit, где это необходимо, а затем, когда я закончу, я могу увидеть, можно ли его" очистить", перемещая инициализации, условия выхода и/или код инкремента внутриfor
's в скобках.
ситуация, когда вам всегда нужно запустить кусок кода один раз, и в зависимости от его результата, возможно, больше раз. То же самое может быть произведено с регулярным
while
цикле.rc = get_something(); while (rc == wrong_stuff) { rc = get_something(); } do { rc = get_something(); } while (rc == wrong_stuff);
do while
Если вы хотите запустить блок кода хотя бы один раз.while
С другой стороны, не всегда будет работать в зависимости от указанных критериев.
Это так просто:
предварительное условие против постусловия
- while (cond) {...} - условие, он выполняет код только после проверки.
- do {...} в то время как (cond) - постусловие, код выполняется по крайней мере один раз.
теперь, когда вы знаете секрет .. используйте их с умом :)
Я вижу, что на этот вопрос был дан адекватный ответ, но хотел бы добавить этот очень конкретный сценарий использования. Вы можете начать использовать делать...хотя и чаще.
do { ... } while (0)
часто используется для многострочных #определяет. Например:
#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 ... в то время как (0) цикл он правильно расширяется к одному блоку:
if (shape == circle) do { area = pi * r * r; volume = area * h; } while (0);
ответы до сих пор суммируют общее использование для do-while. Но ОП попросил пример, так что вот один: получить пользовательский ввод. Но ввод пользователя может быть недействительным - поэтому вы просите ввести, проверить его, продолжить, если он действителен, в противном случае повторите.
с помощью do-while вы получаете вход, пока вход не действителен. С регулярным циклом while вы получаете ввод один раз, но если он недействителен, вы получаете его снова и снова, пока он не станет действительным. Нетрудно заметить, что первый короче, более элегантный и простой в обслуживании, если тело петли становится более сложным.
я использовал его для читателя, который читает ту же структуру несколько раз.
using(IDataReader reader = connection.ExecuteReader()) { do { while(reader.Read()) { //Read record } } while(reader.NextResult()); }
вот моя теория, почему большинство людей (включая меня) предпочитают while () {} loops to do{}while (): a while () {} loop можно легко адаптировать для выполнения как do..в то время как () цикл в то время как обратное не верно. Цикл while является определенным образом "более общим". Кроме того, программисты любят легко понять шаблоны. Цикл while говорит прямо в начале, что его инвариант, и это хорошая вещь.
вот что я имею в виду "более общие" вещи. Возьмите это сделать..цикл while:
do { A; if (condition) INV=false; B; } while(INV);
преобразование этого в цикл while является простым:
INV=true; while(INV) { A; if (condition) INV=false; B; }
теперь, мы берем модель в то время как цикл:
while(INV) { A; if (condition) INV=false; B; }
и превратить это в Сделать..в то время как цикл, дает это чудовище:
if (INV) { do { A; if (condition) INV=false; B; } while(INV) }
теперь у нас есть две проверки на противоположных концах, и если инвариант изменяется, вы должны обновить его в двух местах. В определенном смысле так и делают..пока как специализированные отвертки в Резцовой коробке которую вы никогда не используете, потому что стандарт отвертка делает все, что вам нужно.
я программирую около 12 лет и только 3 месяца назад я встретил ситуацию, когда было действительно удобно использовать do-while, поскольку одна итерация всегда была необходима перед проверкой условия. Так что думаю, ваш большой-время впереди :).
Я не могу представить, как вы никогда не используете
do...while
петли.есть один на другом мониторе прямо сейчас, и в этой программе есть несколько таких циклов. Они все в форме:
do { GetProspectiveResult(); } while (!ProspectIsGood());
while
петли проверьте условие перед циклом,do...while
петли проверьте состояние после цикла. Это полезно, если вы хотите основывать условие на побочных эффектах от выполнения цикла или, как и другие плакаты, если вы хотите, чтобы цикл выполнялся хотя бы один раз.Я понимаю, откуда ты идешь, но
do-while
- это то, что большинство используют редко, и я никогда не использовал сам. Ты не делаешь этого неправильно.вы не делаете это неправильно. Это все равно что сказать кто-то делает это неправильно, потому что они никогда не использовали
byte
примитивно. Это не так часто используется.
я использовал
do while
когда я читаю значение sentinel в начале файла, но кроме этого, я не думаю, что это ненормально, что эта структура не слишком часто используется--do-while
s действительно ситуативны.-- 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)
наиболее распространенный сценарий, с которым я сталкиваюсь, где я использую
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, чтобы прочитать остальную часть файла.
будучи программистом geezer, многие из моих школьных проектов программирования использовали текстовые меню управляемые взаимодействия. Практически все использовали что-то вроде следующей логики для основной процедуры:
do display options get choice perform action appropriate to choice while choice is something other than exit
со школьных дней я обнаружил, что я использую цикл while чаще.
одно из приложений, я видел это в Oracle, когда мы смотрим на результирующие наборы.
Как только у вас есть результирующий набор, Вы сначала извлекаете из него (do) и с этого момента.. проверьте, возвращает ли выборка элемент или нет (пока элемент найден..) .. То же самое может быть применимо для любых других реализаций "fetch-like".
я использовал его в функции, которая возвращала следующую позицию символа в строке utf-8:
char *next_utf8_character(const char *txt) { if (!txt || *txt == '') return txt; do { txt++; } while (((signed char) *txt) < 0 && (((unsigned char) *txt) & 0xc0) == 0xc0) return (char *)txt; }
обратите внимание, что эта функция написана с умом и не проверял. Дело в том, что вы должны делать первый шаг и вы должны сделать это прежде чем вы можете оценить состояние.
любой вид консольного ввода хорошо работает с do-while, потому что вы запрашиваете в первый раз и повторно запрашиваете всякий раз, когда проверка ввода не выполняется.
это довольно распространенная структура в сервер/потребителя:
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) может быть дешевле CPU wise (даже устранение расчета таймаута может быть улучшением с очень высокими скоростями прибытия). Более того, есть много результаты теории очередей, которые делают число обслуживаемых в напряженный период важной статистикой. (См., например, 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 в течение 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 года):In 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
петли и искать ошибки, когда вы видите одно.
я столкнулся с этим, исследуя правильный цикл для использования в ситуации, которую я имею. Я считаю, что это в полной мере удовлетворит общую ситуацию, когда делать.. while loop-это лучшая реализация, чем цикл while (язык C#, поскольку вы заявили, что это ваш основной для работы).
Я создаю список строк на основе результатов SQL-запроса. Возвращенный объект моего запроса представляет собой объект sqldatareader. Этот объект имеет функцию с именем Read (), которая перемещает объект следующая строка данных, и возвращает true, если была другая строка. Он вернет false, если нет другой строки.
используя эту информацию, я хочу вернуть каждую строку в список, а затем остановить, когда больше нет данных для возврата. А Делать... Хотя цикл лучше всего работает в этой ситуации, поскольку он гарантирует, что добавление элемента в список произойдет до проверки наличия другой строки. Причина, по которой это должно быть сделано до проверки 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;