Лучший рефакторинг для ужасного цикла While (True)

Если вы, как и я, дрожите на месте цикла While (True), значит, вы тоже, должно быть, долго и усердно думали о том, как лучше всего его отрефакторить. Я видел несколько различных реализаций, ни одна из которых не была лучше любой другой, например, комбинация таймера и делегата.

Итак, какой лучший способ рефакторинга ужасного цикла While (True) вы придумали или видели?

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


person Jeff Schumacher    schedule 28.10.2008    source источник
comment
Я должен был сформулировать это лучше и подумать о том, чтобы вернуться к этому, но это постоянный процесс обучения, поэтому я решил оставить вопрос как есть, чтобы напомнить себе. :)   -  person Jeff Schumacher    schedule 31.10.2008
comment
Если вы боитесь while (true), вы, должно быть, пишете очень упрощенные алгоритмы. Для большинства сложных алгоритмов требуются циклы с несколькими условиями выхода. Самый естественный способ сделать это - иметь внешний бесконечный цикл, а затем несколько условий выхода, которые выходят из цикла. Использование циклов For или while лучше всего работает, когда у вас есть только одно условие выхода. Хотя это наиболее частый случай, это далеко не универсальная ситуация.   -  person Tyler Durden    schedule 20.02.2017


Ответы (12)


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

Goto раньше был паршивой овцой в стандартах кодирования. Я встречал алгоритмы, в которых goto делал код более читаемым и короче. Иногда не стоит проводить рефакторинг (или лучше использовать goto).

С другой стороны, в большинстве случаев вы можете избегать while (true).

person artur02    schedule 28.10.2008

Я бы предпочел

start:

   // code goes here

goto start;

Это наиболее четко выражает намерение. Удачи в преодолении ваших стандартов кодирования. (Интересно, сколько кармы мне это будет стоить).

person fizzer    schedule 28.10.2008
comment
Как это лучше выражает ваши намерения, чем цикл while(true)? С другой стороны, вы не упомянули недостатки своего решения. Так что да, в общем, это довольно дрянной код. - person Konrad Rudolph; 29.10.2008
comment
Намерение - это безусловное начало ветки. 'goto' специально для этой цели. «Пока» поддерживает условные переходы. Вызывая его с вырожденным условием Always-True и полагаясь на компилятор для его оптимизации, вы лишаетесь дополнительного шага от выражения того, что вы имеете в виду. - person fizzer; 29.10.2008
comment
Что касается недостатков, то технических недостатков нет. Если вам нужен прицел, вставьте скобки. Если нет, не делайте этого. (Предполагается, что C или C ++ - OP не указывает язык). - person fizzer; 29.10.2008
comment
Я проголосовал за это, потому что это казалось правильным решением. for (;;) говорит мне безусловный цикл. - person Tom Hawtin - tackline; 02.12.2008
comment
Тебе понадобится еще goto, чтобы выйти из цикла ... усложняешься? - person Amarghosh; 28.05.2010
comment
@Amarghosh: Если вам нужно выйти из цикла, то на самом деле это не бесконечный цикл; если вы используете while (true) для таких вещей, вы делаете что-то не так. - person Dan Moulding; 18.08.2010
comment
start:...goto start; немного отличается от while(1){...}. Во втором случае break; выходит из цикла, а в первом break; выходит из родительского цикла (или родительского переключателя / случая). - person ceilingcat; 07.09.2017

Чего в этом так страшно? Попробуйте найти общее условие прерывания и преобразовать его в начало цикла. Если это невозможно - хорошо.

person Konrad Rudolph    schedule 28.10.2008
comment
Или выполните рефакторинг, чтобы он был в конце, и воспользуйтесь функцией do while. - person Torlack; 28.10.2008

Когда я сталкиваюсь с циклом while (true), это говорит мне либо

  1. the break condition is not easily tested at the top (or bottom) of the loop,
    • there are multiple break conditions,
    • или предыдущий программист был слишком ленив, чтобы правильно разложить цикл.

1 и 2 означают, что вы также можете придерживаться while (true). (Я использую for(;;), но, на мой взгляд, это стиль.) Я с другим плакатом, чего бояться этого? Я боюсь зацикленных петель, которые прыгают через обручи, чтобы петля скатывалась "должным образом".

person Jim Nelson    schedule 28.10.2008

Зачем рефакторинг? И что же такого "ужасного" в этой конструкции? Он широко используется и хорошо изучен.

Если он не сломан, не чините его.

person Ken Ray    schedule 28.10.2008

Замените True условием, которое вы собирались использовать для выхода из цикла.

В случае службы или фонового потока вы можете использовать:

volatile bool m_shutdown = false;
void Run()
{
    while (!m_shutdown)
    { ... }
}
person Jon B    schedule 28.10.2008
comment
Иногда люди делают это для сервисов, выполнение которых должно продолжаться бесконечно и прерывается только OnStop или исключением. Итак, нет никаких условий. - person Grank; 28.10.2008
comment
Я думаю, что вопрос больше нацелен на полураспространенную практику бесконечного цикла, когда вы на самом деле собираетесь делать это конечное количество раз (игнорируя такие вещи, как потоки и т. Д.), Просто, возможно, конечное условие является сложным. - person Keith Nicholas; 28.10.2008
comment
Далее идет BEGIN X WHILE Y REPEAT, где условие находится в середине цикла. Этот тип конструкции цикла нелегко преобразовать в более традиционный цикл while {} или do {}. - person Torlack; 28.10.2008
comment
Я не согласен; упоминание метода таймера и делегата, казалось бы, предполагает, что вопрос больше нацелен на желаемый неопределенный цикл - person Grank; 28.10.2008
comment
@Grank - Да! Мне нужно отредактировать этот вопрос, чтобы он был более конкретным. - person Jeff Schumacher; 28.10.2008

Ситуация «работать вечно» иногда является частью более крупной машины состояний. Многие встроенные устройства (с бесконечными циклами) на самом деле не работают вечно. У них часто есть несколько рабочих режимов, и они будут последовательно переключаться между этими режимами.

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

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

Я предпочитаю рассматривать «вечный» цикл как действительно один рабочий режим - в какой-то момент в будущем могут быть другие.

someMode= True
while someMode:
    try:
        ... do stuff ...
    except SomeException, e:
        log.exception( e )
        # will keep running
    except OtherException, e:
        log.info( "stopping now" )
        someMode= False

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

person S.Lott    schedule 28.10.2008

#define ever 1
for (;ever;)

?

Мех, просто оставьте это как есть, хотя (правда), вероятно, настолько разборчиво, насколько вы собираетесь получить ...

person Andrew Coleson    schedule 28.10.2008
comment
Я ненавижу такое злоупотребление циклами for. Есть семантика, для которой был разработан цикл for (по сути, семантика от - к семантике), и я думаю, что мы должны придерживаться этого. То, что вы здесь делаете, - это принудительное использование семантики while в ключевом слове for. По крайней мере, #define делает это практически приемлемым. - person Chris Cudmore; 03.11.2008
comment
если мы так безобразны, почему бы не #define FOREVER while (1) или что-то в этом роде :) - person utku_karatas; 12.12.2008
comment
Короче и уродливее: for(;"ever";) - person ShellCode; 26.05.2021

эээ, рефакторинг .....

  • Замените бесконечный цикл бесконечной рекурсией :-)

хорошо, если у вас есть язык, поддерживающий вызовы Tail ....

person Keith Nicholas    schedule 28.10.2008

Если вы хотите, чтобы это продолжалось бесконечно до полного прерывания выполнения программы, я не вижу ничего плохого в while (true). Я столкнулся с этим недавно в службе сбора данных .NET, которая объединила while (true) с thread.sleep, чтобы просыпаться каждую минуту и ​​опрашивать стороннюю службу данных на предмет новых отчетов. Я подумал о его рефакторинге с помощью таймера и делегата, но в конечном итоге решил, что это самый простой и легкий для чтения метод. В 9 случаях из 10 это явный запах кода, но когда нет условия выхода, зачем усложнять задачу?

person Grank    schedule 28.10.2008

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

Подумайте о рекурсии Хассельхоффа.

person Chris Cudmore    schedule 28.10.2008

void whiletrue_sim(void)
  {
    //some code
    whiletrue_sim();
  }

Предупреждение: ваш стек может переполниться.

person 12431234123412341234123    schedule 17.05.2016
comment
Хотя этот блок кода может ответить на вопрос, было бы лучше, если бы вы могли дать небольшое объяснение того, почему он это делает. - person Rakete1111; 18.05.2016
comment
просто прочтите код. Я плохо разбираюсь в словах, и если вы не понимаете этот простой код, я не могу объяснить его лучше. Это просто для того, чтобы ваш стек мог быть переполнен, в зависимости от языка, оптимизатора или того, как работает ваш интерпретатор. - person 12431234123412341234123; 09.06.2016