Точнее, такие случаи имеют право теоретически существовать. Но чот за все годы я не встречал хорошего применения конкретно из под .Net языка.
Говоря что goto плохой стиль - имеется в виду что его использование приводит к плохому стилю, к примеру в случаях когда можно задуматься и прийти к ещё более хорошему минимуму:
К примеру возьмём C:\PABCWork.NET\Samples\!Tutorial\10_Matrices\Matr1.pas:
// Есть ли в матрице элемент 5?
var found5 := False;
for var i := 1 to m do
for var j := 1 to n do
if matr[i,j]=5 then
begin
found5 := True;
goto 1; // Ай как нехорошо! Но это лучший способ выхода из двух вложенных циклов сразу
end;
1:
if found5 then
writeln('Элемент 5 найден')
else writeln('Элемент 5 не найден')
лучший способ выхода из двух вложенных циклов
// Matrix это ещё и статический массив в том примере
function Has5(matr: Matrix): boolean;
begin
Result := true;
for var i := 1 to m do
for var j := 1 to n do
if matr[i, j] = 5 then
exit; // exit это тот же goto, но высокоуровневых и безопасный
Result := false;
end;
if Has5(matr) then
writeln('Элемент 5 найден')
else writeln('Элемент 5 не найден')
Кроме всего прочего - выносить такой код в подпрограммы хорошая практика. В данном случае не очень важно, потому что внешний for стоял прямо в главном begin-end..
Но вообще, у поиска элемента в матрице - 3 уровня вложенности (3 отступа: for+for+if). Поэтому если писать их напрямую - они будут сильно раздувать максимальную вложенность текущей подпрограммы, убивая читабельность.
В общем случае - exit и break продуманы так, чтоб полностью (или на сколько возможно) заменять goto. Но эта гарантия может рассыпаться, если код написан в, и без goto, плохом стиле.
Ну и такая же логика работает (или должна работать) других высокоуровневых фич, заменяющих что то низкоуровневое.
Да, я согласен со всеми замечаниями, но что “лучше” ученики и программисты выберут сами, в зависимости от ситуации. Если сказать честно, то мы ввод “на проверку дурака” делали с помощью goto, при помощи цикла с предусловием wile, при помощи цикла с постусловием repeat - until и даже при помощи цикла с параметром, где на десятом шаге неправильного ввода месяца следует фраза, что “Тебе мальчик нужно в больничку!!!”. Дети, используя многообразие подходов, сами выберут оптимальный и напишут даже лучше, чем преподаватели задумали!!!
## var i: integer;
while not TryRead(i, 'Номер месяца:') or not (i in 1..12) do
'Неправильный ввод!'.Println;
case i of
1..2, 12: 'Зима'.Print;
3..5: 'Весна'.Print;
6..8: 'Лето'.Print;
9..11: 'Осень'.Print;
else raise new System.InvalidProgramException;
end;
В таком порядке дубль условия не страшен, потому что случаи не_совпадения этих условий это:
i in 1..12 не пропускает варианты, которые добавили в case: Это должно быть заметно во время минимального тестирования изменений, которое в любом случае надо провести.
В case не проработан варинт, который добавили в допустимые, к примеру i in 1..13: InvalidProgramException это исключение, которое нельзя поймать с помощью try. + тут даже текст в него передавать не надо - оно и так пишет что неправильно.
Конечно, новые месяцы не могут появится, но я привожу более общий пример хорошего стиля кода.
И давать детям самим найти что работает - прекрасный первый шаг, но преподаватель всё же должен быть хотя бы достаточно способным, чтоб затем показать что можно улучшить.
Считаю, что если Вы только не изучаете историю развития языков программирования, давать десяти - двенадцатилетним детям оператор goto - это плохая идея. Зачем забивать голову тем, от чего уже отошли все современные языки программирования? А вдруг кому-то это покажется проще, чем подумать и написать код без goto и он примет такой стиль на вооружение? Да Вы сами потом замучитесь понимать его коды. Вот был (и есть) в языке Фортран оператор
IF(арифметическое_выражение) метка1, метка2, метка3 Фактически, это три GO TO, упакованные в одну строку. Оператор есть, но использовать его не рекомендуется из-за плохой читаемости кода. Несколько таких GO TO - и Вы убьете вечер на понимание алгоритма, зашитого в код из сотни строк.
Нет таких случаев у нормального программиста. У недоучек - есть. Например, когда имеются вложенные циклы и надо выйти из тела самого внутреннего за пределы самого внешнего, а программист писать подпрограммы не умеет. Мне приходилось достаточно много писать на языках, в которых нет понятия меток (и, вследствие этого, операторов переходов по ним). Сюда относятся, например, языки для работы с СУБД. И ни разу не встретил случая, когда бы я пожалел об отсутствии меток.
Такие случаи реально есть. Например, когда надо выйти из самого внутреннего цикла и потом сделать что-то ещё с полученными несколькими значениями. Лишнюю подпрограмму в этом случае писать неэффективно
а вдруг кто-то при обучении программированию в школе решит, что pascal это совершенный язык и об альтернативе не может идти и речи? и таких вопросов можно придумать сколько угодно. ценность таких аргументов явно не на высоте.
тут всё зависит от преподавателя и обучаемого. “хороший стиль программирования” тоже нужно уметь преподать, а не ставить табу на каких то конкретных операторах.
в общем то @Sun_Serega объяснил примерно то, что я и имел ввиду. пересказывать смысла не имеет. как и писать флаги/дополнительные подпрограммы, тем самым раздувая код и логику ещё сильнее(ИМХО)
когда я был студентом, преподаватель с первых дней предупредил нас о том, что мы никогда не должны использовать оператор безусловного перехода. использование этого оператора автоматически означало двойку за лабораторку или на экзамене. c тех пор я 25 лет занимаюсь программированием и за все эти годы ни разу не использовал GOTO. все высокоуровневые языки программирования имеют достаточно средств, позволяющих обойти использование оператора безусловного перехода.
Не нужно бояться оператора безусловного перехода, просто нужно помнить, что с его помощью можно очень легко превратить код в нечитаемую лапшу, но с другой стороны, не смотря на то, что структурных операторов достаточно для того, чтобы записать любой код, goto может улучшить читаемость кода и сократить его длину, если будет использовться правильно.
Например, в конце некой процедуры/функции происходит удаление объектов, используемых этой функцией, освобождение памяти и возвращение управления в вызывающую подпрограмму. В таком случае, можно использовать goto для перехода к началу этой самой пред-выходной очистки в нескольких местах, где требуется завершение функции. В противном случае придётся повторить один и тот же код несколько раз, что способствует накоплению ошибок: что-то в одном месте исправлено, в другом аналогичном, нет.
Второй вариант — досрочное завершение нескольких вложенных циклов. Для одного цикла есть break, но циклов из которых надо выйти досрочно может быть более одного.
Если преподаватель не смотрел, для чего именно и как использовался этот оператор и ставил двойку — он был неправ. Из серии “слышал звон, да не понял, где он”. Действительно, чрезмерное использование оператора goto где не надо ведёт к появлению трудночитаемого и тяжело отлаживаемого кода, но не просто же так разработчики языков программирования его оставили.
Но это относится не только к goto но и практически ко всему остальному — если использовать где не надо, то это плохо, а где надо — хорошо.
если нужно повторить один и тот же код несколько раз то оформите его в виде процедуры или функции…
д
для этого существуют циклы с пост- и предусловием и досрочный выход можно регулировать с помощью флагов.
у меня противоположная точка зрения
поверьте мне на слово этот преподаватель был довольно высококлассным специалистом. это один из преподавателей который в моей памяти остался как один из лучших за 5 лет учебы.
Циклы у которых есть и пост- и предусловие допустимы только в ограниченном множестве языков. Да, можно взять дополнительную переменную-флаг и проверять её в заголовке каждого из вложенных циклов, но это значительно усложнит и удлинит код.
И как вы в виде процедуры или функции оформите освобождение памяти, используемой вызывающей функцией, с последующим выходом из неё? Может в каких-то языках так и можно извратиться, но это явно не добавит коду понятности и читаемости.
Да даже в этой теме обмусолили уже всё. Выстрелить в ногу можно из чего угодно. Выделять goto как наиболее опасный смысла не имеет. Всё зависит от задачи и квалификации программиста. А не использовать какие-то средства языка, если они способы облегчить код и нагрузку на железо просто расточительно. Естественно, что реальная необходимость в goto - не частый случай. Однако это не означает, что метки не имеют права на жизнь
Освобождение ресурсов должно быть в методе Finallize объектов, выделивших эти ресурсы. Обычно это особождение можно вызвать и вручную, но это уже необязательная часть.
И про вложенные for я уже сказал, и всё ещё считаю что подпрограммы это лучшее решение. Инлайнинг никто не отменял. А с ним exit превращается в тот же GoTo, но более читабельный.
GoTo может иметь смысл, но только как оптимизация и только для тех кто понимает что делает, а не альтернатива принятой структуре кода.
Освобождение ресурсов должно быть в методе Finallize объектов, выделивших эти ресурсы. Обычно это особождение можно вызвать и вручную, но это уже необязательная часть.
А если ресурсы выделены были с помощью обычного malloc из libc без всяких объектов?
Где? Я не видел. Но да, в циклах for даже дополнительное условие воткнуть некуда.
Это говнокод. То что генерирует JIT из создания экземпляра простой одноразовой обёртки, особождающей свои ресурсы в Finallize - не менее эффективно чем ручное управление неуправляемым ресурсом, но при этом освобождает ресурсы не зависимо от исключений. И, при этом, освобождение обычно произойдёт или раньше чем сделали бы вы, или в отдельном потоке GC.