while(1)
{
if (...)
{
some task;
break;
}
if (...)
{
other task;
break;
}
if (...)
{
third task;
break;
}
some else;
}
while(1)
Хотелось бы услышать, что думает прогрессивная программистская общественность о такой конструкции - while(1). Коряво это или нет? Согласитесь, что иногда для выхода из цикла может быть не одно, а несколько самых разнообразных условий, причём в зависимости от конкретного повода для выхода должны выполняться разные действия, так что просто записать их за пределами цикла нельзя.
а что тут корявого? я всегда такую применяю при написании демонов. на С и на Perl. что вызвало вопрос?
break никто еще не отменял.
Точно так же отношусь как и к конструкции for(;;), т.е. ничего против не имею и сам использую, только не while(1), а while(true) , т.к. компилятор C# первую конструкцию не проглотит :)
Ну так понятно все зависит от языка. Где нету true исспользуется 1.
Где есть, даже если true равен 1 как в C++, понятно его надо исспользовать для наглядности.
Интересно, а главный блюститель чистоты языка выскажется?
В данном случае как вариант при задачи какого либо сервера или сервиса не предусматривающего завершения например.
Я хоть и не главный блюститель, но мне это почему-то напоминает анекдот про программиста и шампунь или примеры из учебников по Бейсику 80-х годов.
Хотя, в C#(?) есть ведь цикл do-loop без условия. Мне приходилось использовать repeat until False в Паскале совместно с Break. Но в подобных задачах, как правило, требовалось выполнить тело цикла минимум один раз. Поэтому использование конструкции с предварительным условием для бесконечного цикла наводит на сомнения в правильности ее использования.
я так и знал, что пост - дешевая провокация... :(
Да нет, я же объяснил о чём идёт речь. Конкретизирую.
Код:
Как бы Вы, Уважаемый Freeman, решили бы эту задачу?
В некоторых случаях удобнее и красивее писать while(true).
Есть ещё одна похожая конструкция do {....} while(false), которая заменяет конструкцию try/final.
Иногда и та и другая конструкция превращается в реализацию конечного автомата через switch.
Для прмера приведенного выше, это выглядело бы примерно так:
Код:
while( (state != end) || (state != error) )
{
switch(state)
{
case some:
some task;
state = end;
break;
case other:
other task;
state = end;
break;
case third:
third task;
state = end;
break;
default:
some else;
}
}
{
switch(state)
{
case some:
some task;
state = end;
break;
case other:
other task;
state = end;
break;
case third:
third task;
state = end;
break;
default:
some else;
}
}
Это превращение обусловлено обычно необходимостью "знать" по какому все же условию мы вышли из цикла.
Очень часто такое бывает при инициализации/деинициализации ряда подсистем в составе одной сложной системы. Например, в играх происходит инициализация менеджера памяти, подсистемы ввода/вывода, графической подсистемы, подсистемы звука и т.п. При этом деинициализация по ошибке или по завершению игры должна проходить в обратном порядке, причем при ошибке деинициализация проходит с последней удачно инициализированной системы.
Попробовал в gcc for(;;) и while(1) - ассемблерный код на 100% одинаковый.
ну естественно. gcc оптимизирует код, как и полагается порядочному компилятору из хорошей семьи :)
Ну спасибо. В этот раз вы порадовали меня широтой взглядов.
Код:
for (bool Working = true; Working; )
{
// .................
}
{
// .................
}
А почему не
Код:
for (;true;) {}
Working = false;
и покинуть цикл на следующей итерации.
while(1) вообще нужен тогда, когда выход из цикла является совсем уж исключительным типа "пора бы и компьютер выключить" и все выходы из цикла должны происходить сразу, как только какое-то событие наступило, не дожидаясь завершения итерации. Тогда break выглядит самым правильным решением, ну а если все выходы делаются break'ом, то зачем вводить еще какое-то условие?
Код:
repeat
if <condition_1> then
begin
.
.
end;
if <condition_2> then
begin
.
.
end;
.
.
if <condition_n> then
begin
.
.
end;
until true;
if <condition_1> then
begin
.
.
end;
if <condition_2> then
begin
.
.
end;
.
.
if <condition_n> then
begin
.
.
end;
until true;
Примерно так.
Код:
do
{
// Много команд
}
while (Условие)
Команда2
{
// Много команд
}
while (Условие)
Команда2
Если цикл слишком большой, то его будет трудно читать. И оператор while может восприниматься программистом как условие цикла для Команда2. Просто потому, что программист достал исходники из архива полугодовой давности :(
Я бы не был так категоричен.
Достаточно не писать циклов на несколько экранов (что уже само по себе плохой стиль) и сделать пустую строку после while, и все становиться весьма читабельным.
Код:
while( (state != end) || (state != error) )
{
...
}
{
...
}
[/QUOTE]
Оч интересный цикл. Выход из него происходит тогда, когда state равен одновременно end и error :)
Давайте, не будем придераться к мелочам. На каждого можно накопать целый гербарий таких опечаток и пр. нелепостей. :)
Цитата:
Достаточно не писать циклов на несколько экранов (что уже само по себе плохой стиль)
Ох, мне как-то пришлось доделывать мегапроект :confused: после одного начинающего программиста :eek: ... Хорошо, что он не знал о постусловиях, а то б я вообще опух :D
[QUOTE=Green]сделать пустую строку после while, и все становиться весьма читабельным.[/QUOTE]
В этом согласен- наглядно. Если же продолжать код сразу за постусловием, его сложнее воспринимать даже при коротких циклах.
Код:
repeat
if <condition_1> then
begin
.
.
end;
if <condition_2> then
begin
.
.
end;
.
.
if <condition_n> then
begin
.
.
end;
until true;
if <condition_1> then
begin
.
.
end;
if <condition_2> then
begin
.
.
end;
.
.
if <condition_n> then
begin
.
.
end;
until true;
Примерно так.[/QUOTE]
Ну вы и даёте, Sanila_san! Это же то же самое, что я и написал: формально бесконечный цикл ("until true"), а выходы, очевидно, делаются между begin и end.
Код:
while(1)
{
if (...)
{
some task;
break;
}
if (...)
{
other task;
break;
}
if (...)
{
third task;
break;
}
some else;
}
{
if (...)
{
some task;
break;
}
if (...)
{
other task;
break;
}
if (...)
{
third task;
break;
}
some else;
}
Как бы Вы, Уважаемый Freeman, решили бы эту задачу?[/QUOTE]
В структурном программировании у кождого блока должен быть один вход и один выход(так легче производить разбор кода, код становиться более последовательным), по-этому один из возможных выходов - можно организовать флаг:
Код:
fExit = false;
while(fExit == false)
{
if (...)
{
some task;
fExit = true;
}
if (...)
{
other task;
fExit = true;
}
if(...)
{
third task;
fExit = true;
}
some else;
}
while(fExit == false)
{
if (...)
{
some task;
fExit = true;
}
if (...)
{
other task;
fExit = true;
}
if(...)
{
third task;
fExit = true;
}
some else;
}
Спасибо вам, Фрости, конечно, но то, что вы написали, уже было предложено Грином.
Вы правы неугледел)))))))))))))))))))))
Циклы с условием в конце выполняются несколько быстрее, чем с условиям в начале. (если только компилятор не оптимизирует while(1)), так что лучше наверное do .. while(1); либо по-простому goto :)
Разумеется компилятор (я даже думаю, любой компилятор) оптимизирует while(1). Неужели вы, товарищ с непроизносимым ником, думали, что на уровне машинных инструкций в этом случае будет происходить сравнение единицы с нулём в каждой итерации? Реально сравнение и условный переход будут происходить только при употреблении оператора if. В конце же цикла имеет место безусловный переход.
Цитата:
что на уровне машинных инструкций в этом случае будет происходить сравнение единицы с нулём в каждой итерации? Реально сравнение и условный переход будут происходить только при употреблении оператора if.
Кстати, заметили ли вы, что компилятор (Borland C++ Builder), при компиляции может выводить warning ("условие всегда истина") - возможно, это означает, что код проверки формироваться не будет вообще.
Кончно же не будет.
Надо просто знать как произносить ;))
А потом хз будет или нет оптимизировать - это от компилятора зависит
и, вероятно, настроек сборки. Потенциально, вдруг нам НУЖНО чтобы там была единица, чтобы потом, путем runtime-изменения кода испольняемого, преобразовать ее во чтото еще? )))) (врят ли конечно это нужно, но вдруг)
Да, от настроек зависит.
Это уже другим словом называется.
Ну, не знаю. У меня Builder отбрасывал это сравнение и при выключенной оптимизации. Если какой-то компилятор даст код сравнения на месте while(1), то, по-моему, это какой-то недоделанный компилятор.
Builder 6. Компиляция с отладкой для строки while (true) выдаёт ассемблерный код jmp -0x49 - безусловный переход на верх.
Несмотря на то, что while находится перед циклом, компилятор размещает эту команду после.
Посему рассуждения о преимуществе "постусловия" в исходном тескте можно считать пустым флудом. Ибо, как я уже сказал, while в конце цикла затрудняет чтение и понимание исходника.
ну и так просто эксперименты:
Код:
while(1)
00401004 B8 01 00 00 00 mov eax,1
00401009 85 C0 test eax,eax
0040100B 74 02 je 0040100F
{
}
0040100D EB F5 jmp 00401004
00401004 B8 01 00 00 00 mov eax,1
00401009 85 C0 test eax,eax
0040100B 74 02 je 0040100F
{
}
0040100D EB F5 jmp 00401004
Код:
do
{
}
while(1);
00401004 B8 01 00 00 00 mov eax,1
00401009 85 C0 test eax,eax
0040100B 75 F7 jne 00401004
{
}
while(1);
00401004 B8 01 00 00 00 mov eax,1
00401009 85 C0 test eax,eax
0040100B 75 F7 jne 00401004
Это по поводу того, какой код формируется при отсутствии оптимизации.
Но в глобальном плане это ничего не значит, т.к. подобное нельзя назвать оптимизацией, а тем более, что после оптимизатора код будет одинаковым:
Код:
00401000 EB FE jmp 00401000
Из-за этого первый вариант длиннее на одну команду :D.
Просто - это признак потенциальной ошибки. Лучше использовать состояния.
З.Ы.
Цитата:
почему компилятор занимается проверкой истинности единицы ?
А может ты так задумал))))))
Это что же, MVC такое выдаёт?!!!!
Ничего плохого в псевдо-бесконечных циклах не вижу. Предпочитаю юзать for(;;) . Что касается проверки на еденицу, то в Microsoft C++ 7.1 и 8.0 в Debug режиме это действительно имеет место быть, сделано это вот для чего : как известно студия как 2003 так и 2005 позволяет править код прямо во время выполнения программы, вот это и позволяет вместо единицы туда что-нить другое написать на следующей итерации, например.