Так ли плохи циклы while(true)?

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

do{
     //get some input.
     //if the input meets my conditions, break;
     //Otherwise ask again.
} while(true)

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

Я полностью понимаю ловушки goto и его собрата Java break:label, и у меня есть здравый смысл не использовать их. Я также понимаю, что более полная программа предоставила бы некоторые другие средства спасения, скажем, просто завершить программу, но это не было причиной, которую привел мой профессор, так что...

Что не так с do-while(true)?


person JHarnach    schedule 27.07.2011    source источник
comment
Я не знаю, применим ли тег «домашнее задание», я имел в виду это скорее как вопрос общего программирования, но я приму это.   -  person JHarnach    schedule 27.07.2011
comment
Спросите своего учителя, такие вещи довольно субъективны.   -  person Chris Eberle    schedule 27.07.2011
comment
Я нашел эту статью полезной для понимания что вредного в переходе. Сравнение с break, вероятно, имеет хороший смысл, но на самом деле неправильно понято. Возможно, вы можете обучить этому своего профессора ;) По моему опыту, профессора мало знают о ремесле программирования.   -  person Magnus Hoff    schedule 28.07.2011
comment
Единственная действительно бесспорная плохая вещь в этом, на мой взгляд, тот факт, что do {} while (true) эквивалентно while(true) {}, а последнее является гораздо более традиционной формой и намного яснее.   -  person Voo    schedule 28.07.2011
comment
Если кто-то не ценит простую выразительную мощь break, ему следует попробовать программировать на языке без него. Не нужно слишком много петель, прежде чем вы пожелаете этого!   -  person Steven    schedule 28.07.2011
comment
Я не согласен с тегом домашнего задания.   -  person aaaa bbbb    schedule 28.07.2011
comment
do {} while (true); хорошо сочетается с do {} while (false);. :^)   -  person Tom Hawtin - tackline    schedule 28.07.2011
comment
Перерывы — это не плохо, а часто и хорошо. Помеченные разрывы неплохи, если их использовать осторожно, разумно и очевидно. Ни один из них не эквивалентен goto, который является совершенно произвольным переходом из любого места в любое место.   -  person jprete    schedule 28.07.2011
comment
@jprete, чтобы добавить к вашему комментарию: помеченные разрывы замечательны для выхода из вложенных циклов. Следует избегать вложенных циклов, а также помеченных разрывов. Однако, если они вам нужны, используйте их.   -  person zzzzBov    schedule 28.07.2011
comment
Если бы побег при разрыве был злом, его бы не реализовали в современных языках, которые вместо этого избегают goto. При этом настоящие программисты тоже не боятся использовать GOTO :)   -  person Halberdier    schedule 28.07.2011
comment
ну break только заменяет некоторые ifs и использование флага...   -  person kokbira    schedule 28.07.2011
comment
И (в Delphi) вам нужен Break, чтобы преждевременно завершить цикл for (так как в Delphi вы не можете изменить значение индекса итератора в теле цикла for). Очень полезно. Не хотелось бы прибегать к циклам while для перебора массива или коллекции и прерываться, как только вы нашли то, что искали...   -  person Marjan Venema    schedule 02.08.2011
comment
@Marjan Venema - хорошая мысль. Но наверняка для этого нужен опыт. Программисты, не знакомые с этим плохим трюком, лучше поймут решение while (то есть то, которое заменяет For, а не while true do...break).   -  person TheBlastOne    schedule 11.08.2011
comment
@TheBlastOne: выход из циклов for (и циклов while, если на то пошло) является частью стандартного словаря Delpni. Если вы посмотрите на более чем тривиальный пример кода Delphi, вы столкнетесь с этой конструкцией довольно скоро/часто.   -  person Marjan Venema    schedule 11.08.2011
comment
@Marjan Venema: Да, я не возражал против этого.   -  person TheBlastOne    schedule 11.08.2011
comment
Меня вызвали перед классом, потому что я сделал это на контрольной. Причина, по которой меня вызвали, заключалась в том, что я сделал do while(true), а не потому, что я использовал while(true). Мой профессор сказал, что do while true сбивает с толку при чтении, а поскольку do while true функционально совпадает с while true, нет причин не использовать последний, который легче читать с первого взгляда. Я не согласен с людьми, которые говорят, что использование while true break равносильно использованию логического значения в while. Оператор break принципиально отличается, так как он заканчивается в этом месте.   -  person Jacqlyn    schedule 13.02.2014
comment
Академическая реакция на спагетти-код в языках без более мощных механизмов управления потоком, чем GOTO, была очень сильной, и если вы безоговорочно признаете, что GOTO — плохой оператор, очень легко экстраполировать на то, что любой GOTO-подобный механизм потока плох. Похоже, это произошло здесь. Я обнаружил, что мне редко нужны break/continue, но часто для рефакторинга метода, где они затем заменяются на return.   -  person Thorbjørn Ravn Andersen    schedule 29.07.2015
comment
Re: Что не так с do-while (true)? Помимо того, что while-do более понятен, я думаю, что основная причина, по которой некоторые опытные программисты говорят студентам не использовать while-loops вместо этого использовать for-loops, заключается в том, что при определенных незапланированных условиях while-loop может превратиться в бесконечный цикл, т.е. потенциально состояние гонки, и это намного сложнее/более раздражает. для отладки. Мне сказал это кто-то, чей код я уважаю однажды, но не зацикливался на этом. Поэтому я всегда выполняю рефакторинг до for-loop, если могу. очевидно, что на самом деле это невозможно, если вы хотите прервать цикл, если язык не позволяет break в for-loops.   -  person wide_eyed_pupil    schedule 06.08.2015
comment
@Voo эквивалент? while(..){..} != do {..} while(..), так как выполнение do-while гарантированно выполняется хотя бы один раз!   -  person Martin Pfeffer    schedule 20.03.2017
comment
@MartinPfeffer Я буду помнить об этом на тот момент, когда true начнет оцениваться как false.   -  person Voo    schedule 20.03.2017
comment
Вы должны отправить эту страницу своему профессору по электронной почте :)   -  person Kellen Stuart    schedule 02.05.2018
comment
Ваш учитель дезинформирован, или действительно не информирован. Такое отношение распространено в академических кругах, но не существует здесь, в реальном мире. Это просто не тот случай, когда украшение вашего кода дополнительными булевыми значениями или другими переменными состояния в любом случае лучше, чем использование break, и существуют хорошие формальные аргументы против дополнительного состояния. Аргумент Дейкстры не работает.   -  person user207421    schedule 03.12.2019


Ответы (21)


Я бы не сказал, что это плохо, но в равной степени я бы, по крайней мере, поискал альтернативу.

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

В качестве примера того, где лучше использовать break, чем флаг, рассмотрим:

while (true)
{
    doStuffNeededAtStartOfLoop();
    int input = getSomeInput();
    if (testCondition(input))
    {
        break;
    }
    actOnInput(input);
}

Теперь давайте заставим его использовать флаг:

boolean running = true;
while (running)
{
    doStuffNeededAtStartOfLoop();
    int input = getSomeInput();
    if (testCondition(input))
    {
        running = false;
    }
    else
    {
        actOnInput(input);
    }
}

Я считаю последний более сложным для чтения: у него есть дополнительный блок else, actOnInput имеет больший отступ, и если вы пытаетесь понять, что происходит, когда testCondition возвращает true, вам нужно внимательно просмотреть остальную часть блока. чтобы убедиться, что нет ничего после блока else, которое могло бы произойти независимо от того, установлено ли running значение false или нет.

Оператор break более четко передает намерение и позволяет остальной части блока выполнять то, что ему нужно, не беспокоясь о более ранних условиях.

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

person Jon Skeet    schedule 27.07.2011
comment
Я согласен, что это не первый инструмент, к которому я обращаюсь, но, похоже, он так чисто решает проблему, а мне нравится чистый код. - person JHarnach; 27.07.2011
comment
Являются ли подобные вещи причинами для извлечения части кода из цикла (или вокруг него) и размещения его во внешних методах, что позволяет (буквально) раннее возвращение? - person Clockwork-Muse; 28.07.2011
comment
@X-Zero: Да, иногда. Если вы можете превратить перерыв в возвращение, это часто хорошо... хотя вы все равно можете получить while (true) - person Jon Skeet; 28.07.2011
comment
На самом деле мне больше нравится пример, который вы разместили. Я не согласен с тем, что делать, пока часть, но здесь ваше использование перерыва выглядит нормально и все еще ясно для читателя, что происходит. +1 - person JHarnach; 28.07.2011
comment
На самом деле не так уж сложно убрать беспорядок в неразрывной версии: просто выполните if( !(stop = condition ) ){ dostuff; }, на самом деле, если условие достаточно сложное, можно поместить stop = condition; (или running = condition;) в отдельную строку и повторно использовать значение после этого. на самом деле улучшить читаемость. - person trutheality; 28.07.2011
comment
@trutheality: я предпочитаю, чтобы большая часть моего кода была как можно без отступов. А условие с составным присваиванием кажется мне более сложным. - person Jon Skeet; 28.07.2011
comment
Я всегда стараюсь размещать задания на отдельной строке, и я делаю это на многих языках. Это делает код более понятным (изменение состояния системы — важная вещь), а также очень помогает, если мне нужно подключить отладчик или другой подобный инструмент (трюк, которому я научился у Джона Оустерхаута). . - person Donal Fellows; 28.07.2011
comment
@Donal: я обычно избегаю составных назначений других, кроме как для очень распространенного шаблона while ((line = reader.ReadLine()) != null). - person Jon Skeet; 28.07.2011
comment
Мне даже это особенно не нравится, ТБХ. К счастью, это не так часто встречается. - person Donal Fellows; 28.07.2011
comment
@Donal: мне лично нравится инкапсулировать этот шаблон в итератор, но там, где это сложно сделать, это достаточно знакомый шаблон, и я не возражаю против него. - person Jon Skeet; 28.07.2011
comment
Я не согласен с этим. Условие может сказать вам, что происходит заранее, без необходимости читать код (полезно, если вы бегло смотрите). Разве while(price ‹ priceDiscountThreshhold) не скажет вам больше, чем while(true) ? - person Razor; 03.08.2011
comment
@Vince: Да, и это здорово, если вы можете легко выразить условие в начале цикла. Но иногда вы не можете - и это ситуация, о которой мы говорим. Обратите внимание на первые пару предложений моего ответа. - person Jon Skeet; 03.08.2011
comment
@Исмаил: надеюсь, что нет. Посты следует оценивать исключительно по их содержанию, а не по автору. Черт возьми, я бы даже понизил пост Эрика Липперта, если бы я чувствовал, что он был неточным / бесполезным. Хотя этого еще не было :) - person Jon Skeet; 04.08.2011
comment
Чем больше кода от других людей пришлось прочитать в своей жизни, тем больше +1 этот ответ. (И наоборот.) - person TheBlastOne; 11.08.2011

AFAIK ничего, на самом деле. У учителей просто аллергия на goto, потому что где-то слышали, что это очень плохо. В противном случае вы бы просто написали:

bool guard = true;
do
{
   getInput();
   if (something)
     guard = false;
} while (guard)

Что почти одно и то же.

Может быть, это чище (потому что вся информация о цикле содержится в верхней части блока):

for (bool endLoop = false; !endLoop;)
{

}
person El Marcel    schedule 27.07.2011
comment
Мне нравятся ваши предложения, так как условие прекращения гораздо более заметно. Это означает, что при чтении кода вы быстрее поймете, что он пытается сделать. Я бы никогда не использовал бесконечный цикл, но часто использовал ваши две версии. - person Sled; 28.07.2011
comment
Все согласны с тем, что флаг лучше, чем сломаться, в основном потому, что он выражает намерение? Я вижу, что это выгодно. Все твердые ответы, но я отмечу это как принятое. - person JHarnach; 28.07.2011
comment
Оба они по-прежнему страдают от загрязнения остальной части цикла (по крайней мере, в некоторых ситуациях) из-за необходимости продолжать идти до конца тела цикла, даже если вы знаете, что нарушаете. Я приведу пример в своем ответе... - person Jon Skeet; 28.07.2011
comment
Вы можете просто поставить if в конце, как в вашем примере. На самом деле раньше у меня была аллергия на такого рода конструкции, потому что я всегда связывал циклы с итераторами и увеличивающими значениями, но теперь я вижу значение объявления цикла var и проверку прямо рядом с FOR. Он аккуратный, он снаружи брекетов. Это не всегда лучшее решение, но в некоторых случаях полезное. - person El Marcel; 28.07.2011
comment
Мне больше нравится ответ Джона Скита. Кроме того, while (true) {} НАМНОГО лучше, чем do {} while(true) - person aaaa bbbb; 28.07.2011
comment
ха-ха, правда :) они все равно работают одинаково. - person El Marcel; 28.07.2011
comment
Или просто используйте функцию: while(getSomeInput()), вместо break используйте return. - person Wedge; 28.07.2011
comment
Я ненавижу то, что Дейкстра когда-либо написал ту статью GoTo. Хотя GoTo, безусловно, часто злоупотребляли в прошлом, это не означает, что вы никогда не должны его использовать. Кроме того, Exit For, Break, Exit While, Try/Catch — это всего лишь специализированные формы Goto. Gotos часто может сделать код более читабельным. И да, я знаю, что все можно сделать без Гото. Это не значит, что должно. - person Kibbee; 28.07.2011
comment
@Kibbee: список специализированных форм goto идет дальше: if, цикл for без разрыва, цикл while без разрыва, break, continue, вызовы функций, исключения, .... Если вы удалите все формы скрытого goto, я думаю, что у вас останется язык, который не является полным по Тьюрингу. - person Lie Ryan; 28.07.2011
comment
@Lie Ryan: Точно, свинью можно накрасить губами, но это все равно свинья. Я видел достаточно кода (хорошего и плохого) в свое время, чтобы знать, что Goto не является ни достаточным, ни необходимым для плохого кода. Даже в старом Basic не было вызовов функций, только GOSUB, который был очень простым шагом вперед по сравнению с GoTo, и этот ON ERROR GoTo был единственным механизмом обработки ошибок в VB до тех пор, пока он не превратился в VB.Net. - person Kibbee; 28.07.2011
comment
если вы работаете в среде с малым объемом памяти, лучше избегать создания переменных и проверок условий... - person kokbira; 28.07.2011
comment
GOTO - это плохо? Код ассемблера/машинного языка, в который превращаются наши программы, пронизан эквивалентом GOTO. - person oosterwal; 29.07.2011
comment
Ваш второй пример можно сократить до for (;;), что на самом деле является обычным способом сделать это на некоторых языках (например, C++) - person Billy ONeal; 02.08.2011
comment
Да, конечно, но вам все равно нужно куда-то поместить этот код. Я пытался показать, что структура for может использоваться (ab) не только для итераторов и подсчета фиксированных чисел. - person El Marcel; 03.08.2011
comment
Короче будет guard = condition :-) - person caylee; 24.10.2017

У Дугласа Крокфорда было замечание о том, что он хотел бы, чтобы JavaScript содержал структуру loop:

loop
{
  ...code...
}

И я не думаю, что Java будет хуже, если будет иметь структуру loop.

В циклах while(true) нет ничего плохого, но часто учителя не одобряют их. С точки зрения преподавания очень легко заставить учащихся создавать бесконечные циклы и не понимать, почему цикл никогда не выходит из него.

Но они редко упоминают, что все механизмы циклов могут быть воспроизведены с помощью while(true) циклов.

while( a() )
{
  fn();
}

такой же как

loop
{
  if ( !a() ) break;
  fn();
}

и

do
{
  fn();
} while( a() );

такой же как:

loop
{
  fn();
  if ( !a() ) break;
}

и

for ( a(); b(); c() )
{
  fn();
}

такой же как:

a();
loop
{
  if ( !b() ) break;
  fn();
  c();
}

Пока вы можете настроить циклы так, чтобы они работали, выбранная вами конструкция не имеет значения. Если он случайно подходит для цикла for, используйте цикл for.

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

person zzzzBov    schedule 28.07.2011
comment
+1: есть дополнительные сложности с циклами for, когда задействованы операторы continue, но они не являются основным расширением. - person Donal Fellows; 28.07.2011
comment
Я обычно использую цикл for, когда есть последовательность, которую я хочу выполнить, и цикл while, когда есть условие, которое я хочу выполнить. Это делает код более разборчивым. - person dascandy; 28.07.2011
comment
for (;;) { больше никто не пишет? (произносится навсегда). Раньше это было очень популярно. - person Dawood ibn Kareem; 31.12.2014

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

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

Очевидно, что это не единственная парадигма программирования, но часто ее можно легко применить к другим парадигмам, таким как объектно-ориентированное программирование (например, Java).

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

Хотя в реализации, использующей break, нет ничего изначально «неправильного», некоторые считают, что значительно легче читать код, в котором условие для цикла явно указано в условии while(), и устраняются некоторые возможности чрезмерной хитрости. Определенно существуют ловушки при использовании условия while(true), которые, кажется, часто появляются в коде у начинающих программистов, например, риск случайного создания бесконечного цикла или создания кода, который трудно читать или излишне запутывать.

По иронии судьбы, обработка исключений — это область, в которой отклонения от структурного программирования, безусловно, появятся и будут ожидаемы по мере того, как вы будете углубляться в программирование на Java.

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

person Jessica Brown    schedule 28.07.2011
comment
Вот статья Эдсгера Дейкстры. Это хорошее чтение. - person Roy Prins; 27.04.2018

Обычное соглашение Java для чтения ввода:

import java.io.*;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String strLine;

while ((strLine = br.readLine()) != null) {
  // do something with the line
}

И обычное соглашение С++ для чтения ввода:

#include <iostream>
#include <string>
std::string data;
while(std::readline(std::cin, data)) {
  // do something with the line
}

А в C это

#include <stdio.h>
char* buffer = NULL;
size_t buffer_size;
size_t size_read;
while( (size_read = getline(&buffer, &buffer_size, stdin)) != -1 ){
  // do something with the line
}
free(buffer);

или если вы уверены, что знаете длину самой длинной строки текста в вашем файле, вы можете сделать

#include <stdio.h>
char buffer[BUF_SIZE];
while (fgets(buffer, BUF_SIZE, stdin)) {
  //do something with the line
}

Если вы проверяете, ввел ли ваш пользователь команду quit, легко расширить любую из этих трех структур цикла. Я сделаю это на Java для вас:

import java.io.*;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;

while ((line = br.readLine()) != null  && !line.equals("quit") ) {
  // do something with the line
}

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

person Ken Bloom    schedule 28.07.2011
comment
На самом деле, если вы используете цикл while (true) вместо одного из этих обычных циклов ввода, вы можете забыть проверить конец файла. - person Ken Bloom; 28.07.2011
comment
Вы достигаете этого эффективно, вставляя первую часть цикла в условное выражение while. В финальном состоянии Java он уже становится довольно здоровенным, и если вам пришлось провести обширные манипуляции, чтобы решить, продолжать или нет, это может занять довольно много времени. Вы можете выделить его в отдельную функцию, и это может быть лучше. Но если вы хотите, чтобы это было в одной функции, и есть нетривиальная работа, которую нужно выполнить, прежде чем решить, следует ли продолжать, while(true) может быть лучше. - person poolie; 28.07.2011
comment
@poolie: Тогда, вероятно, лучше всего использовать команду чтения в качестве условия цикла (которое проверяет наличие EOF) и проверять другие условия внутри цикла в виде операторов прерывания. - person Ken Bloom; 28.07.2011
comment
Как бы то ни было, gets() не является общепринятым соглашением. Это очень способствует переполнению буфера. fgets(buffer, BUFFER_SIZE, file) больше похоже на стандартную практику. - person Dave; 03.08.2011
comment
@Dave: теперь я отредактировал ответ, чтобы использовать fgets. - person Ken Bloom; 03.08.2011
comment
Я думаю, что в C вы действительно хотите сохранить результат чтения, чтобы увидеть, сколько данных у вас на самом деле... И, пожалуйста, не поощряйте abusing namespace std; в C++ :-) - person Kerrek SB; 23.01.2012
comment
@KerrekSB fgets так не работает. Из справочной страницы: gets() и fgets() возвращают s [буфер, который вы передали] в случае успеха и NULL в случае ошибки или когда происходит конец файла, когда символы не были прочитаны. - person Ken Bloom; 23.01.2012
comment
@KenBloom: О, извините, перепутал с fread... эээ, так как узнать, есть ли у вас целая строка? Я думаю, вам нужно проверить, где находится NULL? - person Kerrek SB; 23.01.2012
comment
@KerrekSB: вы вызываете getline, который динамически выделяет собственную память. Я обновлю свой ответ. - person Ken Bloom; 23.01.2012

Это не такая уж страшная вещь, но при кодировании нужно учитывать других разработчиков. Даже в школе.

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

При этом вы все еще будете видеть подобные вещи в МНОЖЕСТВЕ кода в реальном мире.

person rfeak    schedule 27.07.2011
comment
Если цикл начинается с while (true), совершенно очевидно, что внутри него будет break или return, или что он будет работать вечно. Быть прямолинейным важно, но while(true) само по себе не так уж плохо. Переменные, которые имеют сложные инварианты в итерациях цикла, могут быть примером чего-то, что вызывает гораздо больше беспокойства. - person poolie; 28.07.2011
comment
Попробуйте углубиться в поиск на три уровня, а затем каким-то образом вырваться наружу. Код будет намного хуже, если вы просто не вернетесь с этой точки. - person dascandy; 28.07.2011

Это твой пистолет, твоя пуля и твоя нога...

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

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

Почему? Мне нужно было выяснить, почему приложение 700K LOC постепенно начинает сжигать 100% процессорного времени, пока каждый процессор не будет загружен. Это был удивительный цикл while (настоящий). Он был большим и неприятным, но сводился к следующему:

x = read_value_from_database()
while (true) 
 if (x == 1)
  ...
  break;
 else if (x ==2)
  ...
  break;
and lots more else if conditions
}

Не было конечной else ветки. Если значение не соответствовало условию if, цикл продолжал работать до конца времени.

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

ИМХО, использование таких конструкций, как while(true), не является хорошим защитным программированием. Он вернется, чтобы преследовать вас.

(Но я помню, как профессора снижали оценки, если мы не комментировали каждую строку, даже для i++;)

person jqa    schedule 29.07.2011
comment
+1 за комментарий о защитном программировании. - person JHarnach; 29.07.2011
comment
В вашем примере код глупый не из-за выбора while(true), а из-за идеи зацикливания кода. - person Florian F; 31.08.2014
comment
Действительно, какой смысл был в этой петле? :) - person juzzlin; 21.11.2014

Это плохо в том смысле, что структурированные конструкции программирования предпочтительнее (несколько неструктурированных) операторов break и continue. Для сравнения, по этому принципу они предпочтительнее «goto».

Я всегда рекомендую делать ваш код как можно более структурированным... хотя, как указывает Джон Скит, не делайте его более структурированным!

person Patrick87    schedule 27.07.2011

По моему опыту, в большинстве случаев циклы имеют «основное» условие для продолжения. Это условие должно быть записано в сам оператор while(). Все остальные условия, которые могут нарушить цикл, являются второстепенными, не столь важными и т. д. Их можно записать в виде дополнительных операторов if() {break}.

while(true) часто сбивает с толку и менее читаем.

Я думаю, что эти правила охватывают не 100% случаев, а, наверное, только 98%.

person AlexR    schedule 27.07.2011
comment
Хорошо сказано. Это похоже на использование цикла for с: while(true) {i++;...}. Вы прячете условие цикла внутри цикла, а не в «подписи». - person LegendLength; 07.01.2016

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

Что касается вашего вопроса: нет внутренней проблемы с

while(true) {
   do_stuff();
   if(exit_time) {
      break;
   }
}

... если вы знаете, что делаете, и следите за тем, чтобы exit_time в какой-то момент оценивалось как true.

Учителя не рекомендуют вам использовать while(true), потому что до тех пор, пока вы не осознаете, что делаете, это простой способ совершить критическую ошибку.

person Shadur    schedule 28.07.2011
comment
Я думаю, что установка exit_time = false; while(!exit_time) { execute_stuff(); } и do { execute_stuff(); } while(! exit_time ); намного понятнее, чем установка if( condition ) { break; } в конце цикла с while(true). Разрывы - это короткие замыкания на циклы - прекрасно, когда они используются в качестве короткого замыкания в середине цикла, но вы должны просто оценить условие в операторе while по сравнению с разрывом в конце цикла. - person dr jimbob; 29.07.2011
comment
По поводу этого комикса: пока делай-пока не можешь делать все то, что пока может, делай-пока иногда лучше делать то, что делай-пока может делать даже то, что пока может делать. - person Theraot; 08.02.2014

Вы можете просто использовать логический флаг, чтобы указать, когда закончить цикл while. Break и go to были причинами того, что программное обеспечение трудно поддерживать - программный кризис (tm) - и его следует избегать, и его легко можно избежать.

Вопрос в том, прагматичен ты или нет. Прагматичные программисты могут просто использовать break в этой простой ситуации.

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

person Daniel Leschkowski    schedule 27.07.2011
comment
Потому что по моему опыту сохранение логического флага, вероятно, не яснее и может быть менее ясным? - person Jon Skeet; 27.07.2011
comment
@Jon Skeet Ну, это вопрос о том, чтобы следовать хорошей привычке или тренировать себя с плохой, используя перерыв. Бул под названием бег вам не понятен? Это очевидно, легко отлаживать, и, как я уже упоминал ранее, полезно сохранить хорошую привычку. В чем проблема? - person Daniel Leschkowski; 28.07.2011
comment
Бул, называемый run, который затем требует, чтобы я имел if (running) внутри цикла, отступ для всего остального кода, когда все, что я хочу, это выйти из цикла, определенно менее понятен для меня, чем простой оператор break в котором указано точно что я хочу сделать. Похоже, вы рассматриваете перерыв как плохую привычку аксиоматически — я так не считаю. - person Jon Skeet; 28.07.2011
comment
Зачем вам иметь if (работает) внутри цикла? Как и в случае с break в конце цикла, вы проверяете, хотите ли вы вырваться и перевернуть флаг вместо использования break и использовать флаг во время (работает) вместо пока (true). Я не понимаю твою мысль, серьезно. Я согласен с тем, что прагматичный кодер может использовать break в определенных ситуациях, но я действительно не понимаю комментариев, которые вы сделали по моему предложению. - person Daniel Leschkowski; 28.07.2011
comment
Вы предполагаете, что вам не нужно ничего делать в цикле после того, как вы определили, выходить или нет. Например, в ситуации с ОП ему нужно попросить больше информации, если он не уходит. - person Jon Skeet; 28.07.2011
comment
Как я понял, в сообщении об операции просить больше — это петля. И в этой конкретной ситуации разрыв не более понятен, чем флаг. - person Daniel Leschkowski; 28.07.2011
comment
Я добавил пример в свой ответ, чтобы показать, что именно я имею в виду. И нет, я не буду болтать, потому что я буду заниматься другими делами. Перемещение с места на место плохо работает с чатом. - person Jon Skeet; 28.07.2011
comment
Просьба о большем является последним комментарием блока, другими словами, кодом, который должен выполняться только если мы не вышли из цикла. - person Jon Skeet; 28.07.2011
comment
Хотя я не буду его использовать, и хотя я не буду использовать оператор break, поскольку я - личное мнение - думаю, его не сложнее читать (насколько я понимаю, код ясно говорит мне в обоих случаях - с разрывом или без него), плохо дать вам голос, потому что я вижу, что вы имеете точку зрения. Которым, по моему скромному мнению, я не обязан делиться, но понимаю, что для некоторых это может быть правильным решением. Лично я считаю, что хорошие привычки нужно воспитывать и сохранять, если нет недостатков. И я не вижу ни одного, но когда вы учите, используя break, вы можете привести новичков к неправильному использованию break. - person Daniel Leschkowski; 28.07.2011
comment
Оператор break означает именно это: выход из цикла, ничего больше. Логическим значением можно манипулировать, проверять и устанавливать в нескольких местах, и люди должны отслеживать свое взаимодействие, чтобы увидеть, действительно ли цикл завершается и когда. При прочих равных break проще. - person poolie; 28.07.2011
comment
Что вы, кажется, упускаете из виду, так это определенные вещи, такие как вложенные циклы, где break усложняет следование коду, а не упрощает его, как вы, кажется, всегда делаете. И если вы испортите свой код запутанным использованием флага, это еще одна проблема... - person Daniel Leschkowski; 28.07.2011
comment
Еще одна причина хороших привычек — постоянство. Если вы используете другой стиль для маленьких и больших циклов, это просто еще одна вещь, о которой нужно подумать. - person LegendLength; 07.01.2016

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

person Griff Sob Becker    schedule 04.08.2011

Нет серьезных проблем с операторами while(true) с операторами break, однако некоторые могут подумать, что это немного снижает читаемость кода. Старайтесь давать переменным осмысленные имена, оценивайте выражения в нужном месте.

В вашем примере кажется намного понятнее сделать что-то вроде:

do {
   input = get_input();
   valid = check_input_validity(input);    
} while(! valid)

Это особенно верно, если цикл do while становится длинным — вы точно знаете, где выполняется проверка, чтобы увидеть, не происходит ли дополнительная итерация. Все переменные/функции имеют соответствующие имена на уровне абстракции. Оператор while(true) говорит вам, что обработка не там, где вы думали.

Возможно, вам нужен другой вывод во второй раз в цикле. Что-то типа

input = get_input();
while(input_is_not_valid(input)) {
    disp_msg_invalid_input();
    input = get_input();
}

тогда мне кажется более читаемым

do {
    input = get_input();
    if (input_is_valid(input)) {
        break;
    }
    disp_msg_invalid_input();
} while(true);

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

person dr jimbob    schedule 28.07.2011

Может быть, мне не повезло. А может мне просто не хватает опыта. Но каждый раз, когда я вспоминаю, что имел дело с while(true), имеющим break внутри, можно было улучшить код, применяя метод извлечения в while-block, который сохранил while(true), но (по совпадению?) преобразовал все break в return.

По моему опыту while(true) без перерывов (т.е. с возвратами или бросками) вполне удобны и понятны.


  void handleInput() {
      while (true) {
          final Input input = getSomeInput();
          if (input == null) {
              throw new BadInputException("can't handle null input");
          }
          if (input.isPoisonPill()) {
              return;
          }
          doSomething(input);
      }
  }
person gnat    schedule 28.07.2011

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

DWORD dwError = ERROR_SUCCESS;

do
{
    if ( (dwError = SomeFunction()) != ERROR_SUCCESS )
    {
         /* handle error */
         continue;
    }

    if ( (dwError = SomeOtherFunction()) != ERROR_SUCCESS )
    {
         /* handle error */
         continue;
    }
}
while ( 0 );

if ( dwError != ERROR_SUCCESS )
{
    /* resource cleanup */
}
person Jim Fell    schedule 28.07.2011

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

person Petey B    schedule 27.07.2011

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

person Paul    schedule 29.07.2011

Для меня проблема в читабельности.

Оператор while с истинным условием ничего не говорит вам о цикле. Это значительно усложняет работу по его пониманию.

Что было бы легче понять из этих двух фрагментов?

do {
  // Imagine a nice chunk of code here
} while(true);

do {
  // Imagine a nice chunk of code here
} while(price < priceAllowedForDiscount);
person Razor    schedule 03.08.2011

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

person Bimal Sharma    schedule 29.07.2011

1) Нет ничего плохого в do -while(true)

2) Ваш учитель не прав.

НСФС!!:

3) Большинство учителей - учителя, а не программисты.

person Pacerier    schedule 29.07.2011
comment
@Коннелл, подожди, пока это услышит твой учитель! - person Pacerier; 03.08.2011
comment
Я самоучка на фронте программирования. Могу судить только по моему учителю информатики в средней школе, который научил нас делать сайты с помощью Dreamweaver таким образом, чтобы ВСЕ было абсолютно позиционированным ди... - person Connell; 04.08.2011
comment
@Connell вверх мой пост, чтобы показать свое согласие - person Pacerier; 04.08.2011
comment
Кто не умеет, научите. - Это довольно большое обобщение, не так ли? Должны ли врачи, инженеры, пилоты и т. д. быть самоучками, поскольку их инструкторы, по-вашему, некомпетентны? - person filip-fku; 05.08.2011
comment
@filip-fku вау вау~ круто, круто! - person Pacerier; 05.08.2011
comment
Извините, что получилось более серьезное звучание, чем я предполагал! Я просто подумал, что огульные обобщения несправедливы. Конечно, есть плохие учителя, но есть и очень хорошие — как и в любой карьере. - person filip-fku; 05.08.2011
comment
Я сам не делал обобщений, это довольно распространенная фраза, особенно в моем старом колледже. Извините, если кого обидел. См. blogcritics.org/culture/article/those-who-cant- делать-учить. - person Connell; 11.08.2011

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

person Dmitry    schedule 02.08.2011