Ошибка ли это?

Точнее, такие случаи имеют право теоретически существовать. Но чот за все годы я не встречал хорошего применения конкретно из под .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, плохом стиле.

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

4 лайка

Да, я согласен со всеми замечаниями, но что “лучше” ученики и программисты выберут сами, в зависимости от ситуации. Если сказать честно, то мы ввод “на проверку дурака” делали с помощью goto, при помощи цикла с предусловием wile, при помощи цикла с постусловием repeat - until и даже при помощи цикла с параметром, где на десятом шаге неправильного ввода месяца следует фраза, что “Тебе мальчик нужно в больничку!!!”. Дети, используя многообразие подходов, сами выберут оптимальный и напишут даже лучше, чем преподаватели задумали!!! :slight_smile:

Спасибо! Именно для этого форум и придуман, чтобы делиться опытом

Да, это я показываю многообразие применение операторов ученикам 4-6 классов… :slight_smile:

Кстати про алтернативные варианты…

## 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;

В таком порядке дубль условия не страшен, потому что случаи не_совпадения этих условий это:

  1. i in 1..12 не пропускает варианты, которые добавили в case: Это должно быть заметно во время минимального тестирования изменений, которое в любом случае надо провести.
  2. В case не проработан варинт, который добавили в допустимые, к примеру i in 1..13: InvalidProgramException это исключение, которое нельзя поймать с помощью try. + тут даже текст в него передавать не надо - оно и так пишет что неправильно.

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

1 лайк

Считаю, что если Вы только не изучаете историю развития языков программирования, давать десяти - двенадцатилетним детям оператор goto - это плохая идея. Зачем забивать голову тем, от чего уже отошли все современные языки программирования? А вдруг кому-то это покажется проще, чем подумать и написать код без goto и он примет такой стиль на вооружение? Да Вы сами потом замучитесь понимать его коды. Вот был (и есть) в языке Фортран оператор IF(арифметическое_выражение) метка1, метка2, метка3
Фактически, это три GO TO, упакованные в одну строку. Оператор есть, но использовать его не рекомендуется из-за плохой читаемости кода. Несколько таких GO TO - и Вы убьете вечер на понимание алгоритма, зашитого в код из сотни строк.

2 лайка

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

1 лайк

В Java есть break с меткой.

Такие случаи реально есть. Например, когда надо выйти из самого внутреннего цикла и потом сделать что-то ещё с полученными несколькими значениями. Лишнюю подпрограмму в этом случае писать неэффективно

1 лайк

Ну почему же неэффективно? Впрочем, Вы тут поддерживаете Кнута, а я - Дейкстру, вот и вся разница.

1 лайк

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

тут всё зависит от преподавателя и обучаемого. “хороший стиль программирования” тоже нужно уметь преподать, а не ставить табу на каких то конкретных операторах.

в общем то @Sun_Serega объяснил примерно то, что я и имел ввиду. пересказывать смысла не имеет. как и писать флаги/дополнительные подпрограммы, тем самым раздувая код и логику ещё сильнее(ИМХО)

1 лайк

когда я был студентом, преподаватель с первых дней предупредил нас о том, что мы никогда не должны использовать оператор безусловного перехода. использование этого оператора автоматически означало двойку за лабораторку или на экзамене. c тех пор я 25 лет занимаюсь программированием и за все эти годы ни разу не использовал GOTO. все высокоуровневые языки программирования имеют достаточно средств, позволяющих обойти использование оператора безусловного перехода.

1 лайк

И в чём тут смысл?

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

Например, в конце некой процедуры/функции происходит удаление объектов, используемых этой функцией, освобождение памяти и возвращение управления в вызывающую подпрограмму. В таком случае, можно использовать goto для перехода к началу этой самой пред-выходной очистки в нескольких местах, где требуется завершение функции. В противном случае придётся повторить один и тот же код несколько раз, что способствует накоплению ошибок: что-то в одном месте исправлено, в другом аналогичном, нет.

Второй вариант — досрочное завершение нескольких вложенных циклов. Для одного цикла есть break, но циклов из которых надо выйти досрочно может быть более одного.

Если преподаватель не смотрел, для чего именно и как использовался этот оператор и ставил двойку — он был неправ. Из серии “слышал звон, да не понял, где он”. Действительно, чрезмерное использование оператора goto где не надо ведёт к появлению трудночитаемого и тяжело отлаживаемого кода, но не просто же так разработчики языков программирования его оставили.

Но это относится не только к goto но и практически ко всему остальному — если использовать где не надо, то это плохо, а где надо — хорошо.

1 лайк

если нужно повторить один и тот же код несколько раз то оформите его в виде процедуры или функции…

д

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

у меня противоположная точка зрения

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

То есть остальные были ещё хуже?

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

И как вы в виде процедуры или функции оформите освобождение памяти, используемой вызывающей функцией, с последующим выходом из неё? Может в каких-то языках так и можно извратиться, но это явно не добавит коду понятности и читаемости.

Да даже в этой теме обмусолили уже всё. Выстрелить в ногу можно из чего угодно. Выделять goto как наиболее опасный смысла не имеет. Всё зависит от задачи и квалификации программиста. А не использовать какие-то средства языка, если они способы облегчить код и нагрузку на железо просто расточительно. Естественно, что реальная необходимость в goto - не частый случай. Однако это не означает, что метки не имеют права на жизнь

Освобождение ресурсов должно быть в методе Finallize объектов, выделивших эти ресурсы. Обычно это особождение можно вызвать и вручную, но это уже необязательная часть.

И про вложенные for я уже сказал, и всё ещё считаю что подпрограммы это лучшее решение. Инлайнинг никто не отменял. А с ним exit превращается в тот же GoTo, но более читабельный.

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

1 лайк

А для этого лучше его вообще не использовать, от греха подальше.

Освобождение ресурсов должно быть в методе Finallize объектов, выделивших эти ресурсы. Обычно это особождение можно вызвать и вручную, но это уже необязательная часть.

А если ресурсы выделены были с помощью обычного malloc из libc без всяких объектов?

Где? Я не видел. Но да, в циклах for даже дополнительное условие воткнуть некуда.

Это говнокод. То что генерирует JIT из создания экземпляра простой одноразовой обёртки, особождающей свои ресурсы в Finallize - не менее эффективно чем ручное управление неуправляемым ресурсом, но при этом освобождает ресурсы не зависимо от исключений. И, при этом, освобождение обычно произойдёт или раньше чем сделали бы вы, или в отдельном потоке GC.