Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2009.10.11;
Скачать: [xml.tar.bz2];

Вниз

Вложенный цикл   Найти похожие ветки 

 
Евгений Р.   (2009-07-30 18:05) [0]

Можно ли из вложенного цикла подать команду break (или continue) для внешнего цикла?


 
Ega23 ©   (2009-07-30 18:08) [1]

выставить флаг, сделать Break из текущего, во внешнем проверить значегте флага, если нужно - Break


 
Игорь Шевченко ©   (2009-07-30 18:09) [2]

нельзя


 
Евгений Р.   (2009-07-30 18:09) [3]

Спасибо


 
brother ©   (2009-07-31 07:16) [4]

var _Stop: boolean;
...

_Stop:= False;
repeat
 repeat
   if (срочно_выйти) then _Stop;
 until (...) and (_Stop)
until (...) and (_Stop);


 
brother ©   (2009-07-31 07:17) [5]

>   if (срочно_выйти) then _Stop;

те так:
if (срочно_выйти) then _Stop:= True;


 
RWolf ©   (2009-07-31 09:13) [6]

ещё есть оператор goto, не к ночи будь помянут.


 
Palladin ©   (2009-07-31 09:15) [7]

Abort


 
Плохиш ©   (2009-07-31 11:02) [8]


>  until (...) and (_Stop)
> until (...) and (_Stop);
>

Тогда уж or


 
RWolf ©   (2009-07-31 11:25) [9]

вот так борьба за чистоту кода от goto оборачивается захламлением кода и лазейкой для неочевидных глюков :)


 
clickmaker ©   (2009-07-31 15:21) [10]

> if (срочно_выйти) then _Stop:= True;

if (срочно_выйти) then Halt;


 
Б   (2009-07-31 17:32) [11]


> if (срочно_выйти) then Halt;


Это же выход из программы, а не из 2-йного цикла.

1) Проверять булеву переменную.
2) Goto.
3) Метод в методе, в котором нужно вызвать Exit.


 
жж   (2009-08-01 20:45) [12]


> Метод в методе


слон в удаве


 
Б   (2009-08-01 23:35) [13]


> слон в удаве


Почему?
1) Если скорость критична, то это лишний груз.
2) Многие его не любят.


 
brother ©   (2009-08-03 07:38) [14]

> Тогда уж or

ну, да) писАл на скорую руку), но алгоритм понятен)


 
Kolan ©   (2009-08-03 09:10) [15]

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


 
palva ©   (2009-08-03 09:56) [16]

По-моему, если вариант с goto будет проще для понимания и легче читаться, то следует применять goto .


 
brother ©   (2009-08-03 10:48) [17]

по [16] в школе юного бойца не говорили, что использование goto плохой тон?


 
Kolan ©   (2009-08-03 15:18) [18]

palva, вряд ли это будет лучше в данном случае.


 
Ega23 ©   (2009-08-03 15:32) [19]


> по [16] в школе юного бойца не говорили, что использование
> goto плохой тон?


Это миф.


 
palva ©   (2009-08-03 15:47) [20]

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


 
Kolan ©   (2009-08-03 15:51) [21]

Я думаю, palva, что в таком случае goto все сделает еще сложнее. Если алгоритм сложный его лучше прорефакторить использую Извлечение метода.


 
palva ©   (2009-08-03 16:01) [22]

Ну если сделает сложнее, тогда не надо, конечно.


 
Kolan ©   (2009-08-03 16:10) [23]

palva, хотелось бы увидеть пример, когда goto действительно полезен.


 
palva ©   (2009-08-03 19:42) [24]

Я и предлагал goto для решения проблем по сабжу. К примеру, я бы мог написать следующий код:

{$APPTYPE CONSOLE}
type
 TArr = array[1..5,1..5] of Integer;
var
 a: TArr =
 ((1,1,1,1,1),(1,1,1,0,1),(1,1,1,1,1),(1,1,1,1,1),(1,1,1,1,1));
 i, j: Integer;

 procedure FirstZero(const a: TArr; var i,j: Integer);
 var ii, jj: Integer;
 label LBreak;
 begin
   for ii := 1 to 5 do for jj := 1 to 5 do
     if a[ii, jj] = 0 then begin
       i := ii; j := jj;
       goto LBreak;
     end;
   LBreak:
 end;

begin
 FirstZero(a, i, j);
 Writeln(i:3, j:3); //  2  4
end.


 
palva ©   (2009-08-03 21:15) [25]

Когда то я забавлялся паскалем 8000 для IBM 360
http://www.jaymoseley.com/hercules/compilers/contents.htm#PASCAL8000
Исходники компилятора (на Pascal 8000) были просто испещрены подобным применением goto. А что тут удивительного, Pascal 8000 хоть и содержал много любопытных нововведений, (как например оператор forall) но процедуры break не имел. А в компиляторах циклы поиска - это на каждом шагу. А как выходить из цикла, когда найдено? Авторы компилятора не заморачивались введением логической переменной, а выходили через goto. Русское слово "западло" было им неизвестно.


 
Kolan ©   (2009-08-04 10:33) [26]

Во-первых, вот как можно было бы изменить этот код использую Извлечение метода.


program Project1;

{$APPTYPE CONSOLE}
type
 TMatrix = array[1..5, 1..5] of Integer;

var
 Matrix: TMatrix =
 ((1,1,1,1,1),
  (1,1,1,0,1),
  (1,1,1,1,1),
  (1,1,1,1,1),
  (1,1,1,1,1));

 Row, Col: Integer;

{Найти первый ноль в матрице.
*Если ноль найдется, то его координаты будут помещены в Row, Col}
procedure FindFirstZero(const a: TMatrix; var Row, Col: Integer);

{Найти ноль в указаной строке.
*Функция вернет номер колонки, если найдет ноль
 в указанной строке или -1, если не найдет.}
 function FindFirstZeroInRow(const Matrix: TMatrix; const ARow: Integer): Integer;
 var
   TempCol: Integer;
 begin
   Result := -1;
  {Проверить есть ли строка с указанным номером в матрице}
   if (ARow >= Low(Matrix)) and (ARow <= High(Matrix)) then
   begin
     for TempCol := Low(Matrix[ARow]) to High(Matrix[ARow]) do
       if Matrix[ARow, TempCol] = 0 then
       begin
         Result := TempCol;
         Break;
       end;
   end;
 end;

var
 TempRow: Integer;
 ZeroColIndex: Integer;
begin
 Row := -1;
 Col := -1;
 for TempRow := Low(Matrix) to High(Matrix) do
 begin
  {Поискать ноль в строке.}
   ZeroColIndex := FindFirstZeroInRow(Matrix, TempRow);
  {Если ноль найдется — запомнить его координаты и выйти.}
   if ZeroColIndex <> -1 then
   begin
     Row := TempRow;
     Col := ZeroColIndex;
     Break;
   end;
 end;
end;

begin
FindFirstZero(Matrix, Row, Col);
Writeln(Row:3, Col:3); //  2  4
Readln;
end.


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

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

Еще по поводу примера.
На что бы я обратил ваше внимание, если бы, скажем, вы работали у меня.
1. Название функции не содержит глагола. Хорошее название должно отвечать на вопрос «что сделать?». Ваше название FirstZero оставляет неопределенность, хочется спросить: что первый ноль?

2. Ваш код плохо отформатирован. Кроме мелких придирок, вроде маленьких ii, на которые не стоит обращать внимания, в форматировании есть недостаток — его структура не видна. Мало того, что вы вложили один цикл в другой без begin-end"а (конечно я знаю что так можно, но если вдруг во внешний цикл понадобится что-то добавить, то про begin-end можно забыть и потерять 5-10 минут на поиск проблемы), так еще и вытянули их в одну строку.
Ваш код только кажется проще, на самом деле понять его сложнее.

3. Ну и теперь goto. Ваш пример не годится для того, чтобы показать, что goto бывает полезен, так как в данном примере он совершенно бесполезен, потому что есть Exit. Exit позволить сэкономить две строки.


program Project2;

{$APPTYPE CONSOLE}
type
TArr = array[1..5,1..5] of Integer;
var
a: TArr =
((1,1,1,1,1),(1,1,1,0,1),(1,1,1,1,1),(1,1,1,1,1),(1,1,1,1,1));
i, j: Integer;

procedure FirstZero(const a: TArr; var i,j: Integer);
var ii, jj: Integer;
begin
  for ii := 1 to 5 do for jj := 1 to 5 do
    if a[ii, jj] = 0 then begin
      i := ii; j := jj;
      Exit;
    end;
end;

begin
FirstZero(a, i, j);
Writeln(i:3, j:3); //  2  4
end.


Жду более показательный пример.


 
Дмитрий Белькевич   (2009-08-04 13:08) [27]


> 3. Ну и теперь goto. Ваш пример не годится для того, чтобы
> показать, что goto бывает полезен, так как в данном примере
> он совершенно бесполезен, потому что есть Exit


Полностью согласен. Внешняя процедура (причём именно  с двумя циклами, а не вызов внутреннего цикла из внешнего) + exit.

Плюсы: избавляемся от goto (более структурировано), нет постоянных лишних проверок в цикле (что может замедлить работу раза в 2-3), нет вызова внутреннего цикла из внешнего (в принципе, можно inline цикл сделать, но inline не везде есть, да и ограничения у него есть).


 
palva ©   (2009-08-04 13:48) [28]

С интересом прочитал.
Начну с того, что код для работы я всегда пишу в соответствии с корпоративными стандартами. Здесь же я излагаю исключительно свои представления о прекрасном.

Форматирование в одну строку сделано специально. Можно сказать, что это разворачивание некоего макроса, который сидит у меня в голове. И это макрос называется "просмотреть все элементы матрицы". Аналогично этому я пишу в одну строчку три оператора, обменивающие значения у двух переменных. Для меня это тоже единая смысловая конструкция. В некоторых языках имеется и соответствующая языковая конструкция. Вы ведь должны меня извинить, но паскаль это не тот язык на котором я пишу большую часть моего кода. Где есть оператор обмена переменных, я сейчас язык не припомню, а для просмотра элементов массива можно привести пример на популярном языке C#. Например, если требуется подсчитать количество элементов массива равных трем, то мы пишем:
int[,] a = new int[3,3] {{1,2,3},{1,2,3},{1,2,3}};
int n = 0;
foreach(int i in a) if(i == 3) n++;

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

Вы совершенно правы, что в моем примере можно применить оператор Exit, и пример мой уязвимый. Это, действительно, короче. Но откровенно говоря, goto здесь будет понятней. Плохо когда процедура большая, но по факту она бывает на несколько экранов. И когда тупо смотришь в конец и не понимаешь, почему управление туда не доходит, это не очень здорово. Когда в конце стоит метка, то уже можно поискать выше по тексту, откуда туда делаются переходы. Я всегда стараюсь делать так, чтобы процедура доходила до конца и выходила натыкаясь на end. Если преобразовать алгоритм без потери читабельности не удается, то скрепя сердце пишу exit. Но только потому, что короче. Эквивалентная конструкция с goto ExitProc; понятней.

Но пример все равно неудачный, поскольку можно применить exit. Все что я сейчас пытаюсь придумать взамен, легко сводится без потери ясности к варианту без goto. Может быть, что-нибудь придет в голову, тогда напишу еще. Я как-то не могу припомнить случая, когда мне действительно понадобился goto. Единственный случай, когда я применял goto в рабочем коде это консольные утилиты по обработке текстов. Открывается много файлов, при неудачном открытии одного из них выдается сообщение об ошибке.

{$APPTYPE CONSOLE}
var f1, f2, f3: Text;
label Err;
begin
 Assign(f1, "file1.txt");
 Assign(f2, "file2.txt");
 Assign(f3, "file3.txt");
{$I-}
 Reset(f1);
 if IOResult <> 0 then goto Err;
 Reset(f2);
 if IOResult <> 0 then goto Err;
 Rewrite(f3);
 if IOResult <> 0 then goto Err;
{$I+}
 // to do
 exit;
Err:
 WriteLn("Parameter error");
end.

Здесь можно применить try но в данном случае по понятности это будет эквивалент goto.


 
StriderMan ©   (2009-08-04 13:48) [29]

try
 for ... do
   for ... do
      if SomeCondition then
        Abort;
except
 on E: EAbort do begin end;
 else
   raise;
end;


 
StriderMan ©   (2009-08-04 13:49) [30]

PS: вместо Аборт можно для понятности сделать какой-нибудь свой raise EDoubleBreak.Create;


 
palva ©   (2009-08-04 13:55) [31]


> нет постоянных лишних проверок в цикле (что может замедлить работу раза в 2-3)

Здесь я что-то не понял.


 
Дмитрий Белькевич   (2009-08-04 14:06) [32]


> Здесь я что-то не понял.


Две лишние проверки:


> repeat  repeat    if (срочно_выйти) then _Stop;  until (.
> ..) and (_Stop)until (...) and (_Stop);


 
Дмитрий Белькевич   (2009-08-04 14:09) [33]

В 2-3 раза я, наверно, преувеличил, спорить не буду, но замедление будет.


 
palva ©   (2009-08-04 14:15) [34]

Это что, goto неявно приведет к таким проверкам? И будет медленнее?
Я что-то не врубился.


 
Kolan ©   (2009-08-04 14:46) [35]

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

Вчитываюсь я всегда форматируя код так, как понятно мне (а мне понятно так как в «Стандарт стилевого оформления исходного кода Делфи» http://www.delphikingdom.com/asp/viewitem.asp?catalogid=802), отсюда и замечание.


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


Когда я писал ответ вам, то тоже размышлял о том, где же все таки может пригодится goto, поэтому ваш ответ про большую процедуру ожидал.

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

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

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

Пример

Ваш код я бы переработал так:
program Project2;

{$APPTYPE CONSOLE}

procedure ShowError(const Error: string);
begin
 WriteLn(Error);
 ReadLn;
end;

function OpenFile(const AFileName: string; var Error: string): Boolean;
var
 F: Text;
begin
 Result := True;
 Error := "";

 Assign(F, AFileName);
 {$I-}
 Reset(F);
 if IOResult <> 0 then
 begin
   Result := False;
   Error := "Parameter error";
 end;
 {$I+}
end;

var
 f1, f2, f3: Text;
 Error: string;
begin
 if not OpenFile("file1.txt", Error) then
 begin
   ShowError(Error);
   Exit;
 end;

 if not OpenFile("file2.txt", Error) then
 begin
   ShowError(Error);
   Exit;
 end;

 if not OpenFile("file3.txt", Error) then
 begin
   ShowError(Error);
   Exit;
 end;
end.


А теперь представим, что после открытия файлов нужно сделать еще что-то. И именно в конце. Я очень мало пользуюсь goto, поэтому у меня с трудом  получилось изголится нужным образом:
program Project2;

{$APPTYPE CONSOLE}
var f1, f2, f3: Text;
label Err;
label 1;
begin
Assign(f1, "file1.txt");
Assign(f2, "file2.txt");
Assign(f3, "file3.txt");
{$I-}
Reset(f1);
if IOResult <> 0 then goto Err;
Reset(f2);
if IOResult <> 0 then goto Err;
Rewrite(f3);
if IOResult <> 0 then goto Err;
{$I+}
// to do
goto 1;
Err:
WriteLn("Parameter error");
1:
WriteLn("Job is done."); //Это надо сделать именно тут, не зависимо от ошибок.
Readln;
end.


Получается все более и более запутано. В моем варианте я бы мог просто использовать вложенные условия.


program Project2;

{$APPTYPE CONSOLE}

procedure ShowError(const Error: string);
begin
 WriteLn(Error);
 ReadLn;
end;

function OpenFile(const AFileName: string; var Error: string): Boolean;
var
 F: Text;
begin
 Result := True;
 Error := "";

 Assign(F, AFileName);
 {$I-}
 Reset(F);
 if IOResult <> 0 then
 begin
   Result := False;
   Error := "Parameter error";
 end;
 {$I+}
end;

var
 f1, f2, f3: Text;
 Error: string;
begin
 if not OpenFile("file1.txt", Error) then
   ShowError(Error)
 else
   if not OpenFile("file2.txt", Error) then
     ShowError(Error)
   else
     if not OpenFile("file3.txt", Error) then
       ShowError(Error);

 WriteLn("Job is done."); //Это надо сделать именно тут, не зависимо от ошибок.
 Readln;

end.


Но в моем примере есть еще дублирующийся код — вызов ShowError(Error);, если убрать и его, то будет вообще хорошо.

program Project2;

{$APPTYPE CONSOLE}

procedure ShowError(const Error: string);
begin
 WriteLn(Error);
 ReadLn;
end;

function OpenFile(const AFileName: string; var Error: string): Boolean;
var
 F: Text;
begin
 Result := True;
 Error := "";

 Assign(F, AFileName);
 {$I-}
 Reset(F);
 if IOResult <> 0 then
 begin
   Result := False;
   Error := "Parameter error";
 end;
 {$I+}
end;

var
 f1, f2, f3: Text;
 Error: string;
begin
 if   (not OpenFile("file1.txt", Error))
   or (not OpenFile("file2.txt", Error))
   or (not OpenFile("file3.txt", Error))
 then
   ShowError(Error);

 WriteLn("Job is done."); //Это надо сделать именно тут, не зависимо от ошибок.
 Readln;
end.


То есть рефакторинг обычно полезнее goto и если опыта программирования не очень много, то правило не использования goto не так уж плохо.


 
Игорь Шевченко ©   (2009-08-04 14:48) [36]

goto не так вреден, как его малюют. Практически во всех приведенных примерах стремление к избавлению от goto приводит к запутанности кода.

Оно надо - код запутывать ?


 
Anatoly Podgoretsky ©   (2009-08-04 15:01) [37]

> Игорь Шевченко  (04.08.2009 14:48:36)  [36]

Особенно если поставить целью, ни в коем случае не использовать try


 
Kolan ©   (2009-08-04 15:03) [38]

Считаю код последнего моего примера не менее понятен чем код с goto, но он еще удобнее для модификации.

Может быть Игорь приведет пример где goto был бы весьма полезен, может даже из рабочего кода?


 
Игорь Шевченко ©   (2009-08-04 15:39) [39]


> Считаю код последнего моего примера не менее понятен чем
> код с goto


Считай, твое полное право. Код в [28] является и более понятным и удобным для модификации.


> Может быть Игорь приведет пример где goto был бы весьма
> полезен, может даже из рабочего кода?


Весьма сожалею, но рабочего кода с goto у меня нету по одной простой причине - мне лень объявлять label.


 
Дмитрий Белькевич   (2009-08-04 15:41) [40]

>Это что, goto неявно приведет к таким проверкам? И будет медленнее?
Я что-то не врубился.

Нет, это приведенный в [4] код, с двумя лишними проверками, будет медленнее.



Страницы: 1 2 вся ветка

Форум: "Начинающим";
Текущий архив: 2009.10.11;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.59 MB
Время: 0.006 c
2-1249858889
Johnnnnn
2009-08-10 03:01
2009.10.11
Передача файла по нету.


15-1249707770
Savek
2009-08-08 09:02
2009.10.11
Браузер


1-1219124183
checkmate-maker
2008-08-19 09:36
2009.10.11
Редактор с картинками


2-1249564584
leonidus
2009-08-06 17:16
2009.10.11
Вопрос по механизму работы application.ProcessMessages


15-1250063545
brother
2009-08-12 11:52
2009.10.11
Где найти newton headers?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский