Есть ли необходимость в цикле "do {...} while ()"? - PullRequest
66 голосов
/ 15 июня 2009

Бьярн Страуструп (Cjarne Stroustrup) (создатель C ++) однажды сказал, что он избегает циклов "do / while" и предпочитает вместо этого писать код в терминах цикла "while". [См. Цитату ниже.]

Услышав это, я обнаружил, что это правда. о чем ты думаешь? Есть ли пример, когда «do / while» намного чище и проще для понимания, чем если бы вы использовали вместо него «while»?

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

Позвольте мне спросить по-другому: предположим, что вам запрещено использовать «do / while» - есть ли реалистичный пример, в котором у вас не было бы выбора, кроме как писать нечистый код с использованием «while»?

Из "Языка программирования C ++", 6.3.3:

По моему опыту, оператор do является источником ошибок и путаницы. Причина в том, что его тело всегда выполняется один раз, прежде чем условие будет оценено. Однако, чтобы тело работало правильно, что-то очень похожее на состояние должно сохраняться даже в первый раз. Чаще, чем я мог предположить, я обнаружил, что условие не выполняется должным образом ни тогда, когда программа была впервые написана и протестирована, ни позже, после изменения кода, предшествующего ей. Я также предпочитаю условие "там, где я его вижу". Следовательно, я стараюсь избегать дел-заявлений. -Bjarne

Ответы [ 19 ]

2 голосов
/ 13 ноября 2013

Прежде всего, я согласен, что do-while менее читабелен, чем while.

Но я поражен, что после стольких ответов никто не задумывался над тем, почему do-while даже существует в языке. Причина в эффективности.

Допустим, у нас есть цикл do-while с N проверками условий, где результат условия зависит от тела цикла. Затем, если мы заменим его на цикл while, вместо этого мы получим N+1 проверку условий, где дополнительная проверка не имеет смысла. Это не страшно, если условие цикла содержит только проверку целочисленного значения, но допустим, что у нас есть

something_t* x = NULL;

while( very_slowly_check_if_something_is_done(x) )
{
  set_something(x);
}

Тогда вызов функции на первом круге цикла является избыточным: мы уже знаем, что x еще ничего не установлен. Так зачем выполнять какой-то бессмысленный код?

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

2 голосов
/ 15 июня 2009

Прочитайте Теорема структурированной программы . Do {} while () всегда можно переписать на while () do {}. Последовательность, выбор и итерация - все, что когда-либо было необходимо.

Поскольку все, что содержится в теле цикла, всегда может быть заключено в процедуру, грязь необходимости использования while () do {} никогда не станет хуже, чем

LoopBody()
while(cond) {
    LoopBody()
}
2 голосов
/ 15 июня 2009

Цикл do-while всегда можно переписать как цикл while.

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

Лично я предпочитаю циклы while, потому что это упрощает рассуждения об инвариантах цикла IMHO.

Относительно того, существуют ли ситуации, когда вам нужны циклы do-while: вместо

do
{
  loopBody();
} while (condition());

всегда можно

loopBody();
while(condition())
{
  loopBody();
}

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

«Когда в Риме, делай, как римляне».

Кстати: цитата, которую вы ищете, может быть такая ([1], последний абзац раздела 6.3.3):

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

(Примечание. Это мой перевод немецкого издания. Если вы владеете английским изданием, не стесняйтесь редактировать цитату в соответствии с его оригинальной формулировкой. К сожалению, Аддисон-Уэсли ненавидит Google.)

[1] B. Страуструп: Язык программирования C ++. 3-е издание. Addison-Wessley, Reading, 1997.

2 голосов
/ 15 июня 2009

В ответ на вопрос / комментарий от неизвестного (гугл) к ответу Дэна Олсона:

"do {...} while (0) - это важная конструкция для правильного поведения макросов."

#define M do { doOneThing(); doAnother(); } while (0)
...
if (query) M;
...

Вы видите, что происходит без do { ... } while(0)? Он всегда будет выполнять doAnother ().

2 голосов
/ 15 июня 2009

Я почти никогда не использую их просто из-за следующего:

Несмотря на то, что цикл проверяет пост-условие, вам все равно нужно проверить это условие поста в вашем цикле, чтобы не обрабатывать условие пост.

Взять образец псевдокода:

do {
   // get data 
   // process data
} while (data != null);

Звучит просто в теории, но в реальных ситуациях это может выглядеть так:

do {
   // get data 
   if (data != null) {
       // process data
   }
} while (data != null);

Дополнительная проверка "если" просто не стоит ИМО. Я обнаружил очень мало случаев, когда было бы более лаконично делать do-while, а не цикл while. YMMV.

1 голос
/ 19 июня 2013

Ну, может быть, это несколько шагов назад, но в случае

do
{
     output("enter a number");
     int x = getInput();

     //do stuff with input
}while(x != 0);

Возможно, хотя и не обязательно для чтения, использовать

int x;
while(x = getInput())
{
    //do stuff with input
}

Теперь, если вы хотите использовать номер, отличный от 0, чтобы выйти из цикла

while((x = getInput()) != 4)
{
    //do stuff with input
}

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

1 голос
/ 15 июня 2009

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

Если , тогда как было зарезервировано только для , в то время как - петли и до / , в то время как было изменено на do / до или повтор / до , я не думаю, что цикл (который, безусловно, удобен и «правильный» способ кодирования некоторых циклов) вызвал бы так много проблем.

Я уже говорил об этом в отношении JavaScript , который также унаследовал этот печальный выбор от C.

1 голос
/ 15 июня 2009

рассмотрим что-то вроде этого:

int SumOfString(char* s)
{
    int res = 0;
    do
    {
        res += *s;
        ++s;
    } while (*s != '\0');
}

Бывает, что '\ 0' равно 0, но я надеюсь, что вы поняли.

0 голосов
/ 12 февраля 2018

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

int main() {
    char c;

    do {
        printf("enter a number");
        scanf("%c", &c);

    } while (c < '0' ||  c > '9'); 
}

может стать таким:

int main() {
    char c = askForCharacter();
    while (c < '0' ||  c > '9') {
        c = askForCharacter();
    }
}

char askForCharacter() {
    char c;
    printf("enter a number");
    scanf("%c", &c);
    return c;
}

(простите за неправильный синтаксис; я не программист на C)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...