- Операторы перехода break, continue, return
- Оператор break
- break как замена goto
- Оператор continue
- Оператор return
- GOTO в прикладном программировании
- GOTO — свойства и влияние на качество кода
- Особенности GOTO в прикладном программировании
- GOTO — проблемы и варианты исправлений
- Резюме:
- GOTO or not GOTO вот в чём вопрос
- Введение
- Почему ведутся споры
- Доводы «противников» GOTO
- Доводы «защитников» GOTO
- Подведём итоги
- Благодарности
Операторы перехода break, continue, return
В Java есть три оператора перехода: break, continue, return. Операторы перехода передают управление другой части программы.
Оператор break
Оператор break завершает последовательность операторов в операторе switch, позволяет выйти из цикла и в качестве оператора безусловного перехода (goto).
Рассмотрим пример выхода из цикла. Используя оператор break, можно вызвать немедленное завершение цикла, пропуская условное выражение и другой код в теле цикла. Когда приложение встречает оператор break внутри цикла, оно прекращает выполнение цикла и передаёт управление оператору, следующему за циклом.
Получим следующий результат:
Вроде хотели, чтобы цикл for выполнялся от 0 до 100, а сами коварно вышли из него, когда значение переменной i стало равным 4.
Код можно переписать с использованием цикла while:
Результат будет такой же.
При использовании вложенных циклов оператор break осуществляет выход только из самого внутреннего цикла, не оказывая влияния на внешний цикл. Создадим пример с использованием вложенных циклов:
Получим следующий результат:
В примере оператор break прервал выполнение второго цикла, но первый цикл продолжал работать.
В цикле можно использовать несколько операторов break, но увлекаться не стоит. Лучше переделайте код.
break как замена goto
В Java нет оператора безусловного перехода goto. Если вы не знаете, что это такое, то можете смело пропустить данный абзац. У оператора break есть особая форма записи:
Обычно метка — это имя метки, идентифицирующей блок кода. Я ни разу не встречал в реальных примерах данный способ, поэтому не буду приводить пример использования, чтобы вас не запутать.
Оператор continue
Иногда требуется, чтобы повторение цикла начиналось с более раннего оператора его тела. В циклах while и do-while оператор continue вызывает передачу управления непосредственно управляющему условному выражению цикла. В цикле for управление передаётся вначале итерационной части цикла for, а потом условному выражению. При этом во всех циклах промежуточный код пропускается.
В следующем примере выводим два числа в каждой строке:
В этом коде оператор % служит для проверки чётности значения переменной i. Если значение чётное, выполнение цикла продолжится дальше, а если нечётное, то произойдёт переход на новую строку. В результате мы получим следующий текст:
Как и оператор break, оператор continue может содержать метку содержащего его цикла, который нужно продолжить. Создадим пример вывода треугольной таблицы умножения чисел от 0 до 9.
В этом примере оператор continue прерывает цикл подсчёта значений переменной j и продолжает его со следующей итерации цикла подсчёта переменной i. На экране увидим текст в таком виде:
Данный оператор в практике встречается достаточно редко. Но на всякий случай помните о нём. Иногда он может пригодиться.
Оператор return
Оператор return используют для выполнения явного выхода из метода. Оператор можно использовать в любом месте метода для возврата управления тому объекту, который вызвал данный метод. Таким образом, return прекращает выполнение метода, в котором он находится.
Источник
GOTO в прикладном программировании
Картинка из книги Thinking Forth
Мотивы для использования GOTO и альтернативы ему принципиально отличаются для системного и прикладного программирования — это является и важной причиной холиваров. Для прояснения ситуации рассмотрим GOTO только в разрезе прикладного программирования.
Основной тезис: в прикладном программировании GOTO однозначно лучше обходить.
Как докажем:
- В прикладном программировании критически важен один параметр кода — сопровождаемость.
- Goto не ухудшает однозначно сопровождаемость только в небольшом проценте случаев, и даже в этих случаях принципиально от альтернатив не отличается
- Ради небольшого процента случаев его использовать вредно:
1) очень низкоуровневое, поэтому сильно развращает программиста (возникает соблазн использовать и в других местах) — большой вред из-за небольшого процента случаев, когда GOTO можно применить;
2) даже в таких случаях есть более красивые альтернативы.
GOTO — свойства и влияние на качество кода
Параметры качества кода
Общие свойства GOTO:
- неструктурировано: можно вставить в почти произвольное место, сложно понять, как мы туда попали, в отличие от остальных конструкций ветвления;
- закрепощает исходный код: если структурированные блоки можно менять по-разному, перестраивать их порядок, как в конструкторе, то GOTO — это гвоздь, соединяющий какие-то блоки конструктора — после его внедрения код изменить уже очень непросто.
Использование GOTO влияет на оба параметра качества кода и их составляющие. При этом понятно, что потребление ресурсов GOTO сокращает на небольшую константу, в который оно встроено, так как сложность алгоритма оно никогда не меняет.
Что не является GOTO:
- другие конcтрукции управления потоком выполнения — if,switch,while и т.п.: в них всех ветвления потока жестко заданы синтаксисом, находятся на границе того же блока — верхней или нижней (для return — граница функции), а GOTO можно размещать произвольно
- автоматически сгенерированный код — как и в сгенерированном ассемблере, в нем копаться и его поддерживать не приходится.
Особенности GOTO в прикладном программировании
Прикладное программирование здесь — программирование на языках высокого уровня, поддерживающих структурирование кода, в том числе структурный подход к обработке исключений: Java, C#, C++, интерпретируемые языки и т.п. — в общем, стандартный прикладной мэйнстрим. C не рассматриваю как низкоуровневый язык, используемый сейчас в основном для системного программирования.
Особенности прикладного программирования:
- нет необходимости в точечных оптимизациях — отдельных тактов или ячеек памяти, поэтому экономию ресурсов можно отбросить из рассмотрения GOTO — остается только сопровождаемость
- есть возможность как угодно структурировать логику — как угодно объединять в функции/методы, заводить сколько угодно переменных, классов и т.п. с любыми названиями, возможность бросать исключения
GOTO только ухудшает сопровождаемость кода
В системном программировании важна максимальная экономия ресурсов, поэтому там, возможно, применение GOTO для этой цели оправдано.
А в прикладном программировании параметр «потребление ресурсов» можно отбросить, остается только параметр сопровождаемости, который GOTO ухудшает.
GOTO — проблемы и варианты исправлений
Рассмотрим применение GOTO в различных вариантах перемещения по структуре кода и альтернативы ему:
1. Вход в блок извне:
1.1 Вход в «не цикл»:
легко и очевидно переписывается без GOTO:
1.2 Вход в цикл:
нельзя: вообще непонятен поток выполнения:
2. Переход внутри одного блока:
нет необходимости, легко переписывается, обычно на if/else:
3. Выход из блока наружу
Это основной случай возможного применения GOTO. Разобьем его на еще более мелкие и рассмотрим подробно на примерах.
Общий подход — максимально декомпозируем: разбиваем на методы по смыслу, логику фиксируем в флагах с говорящими названиями — получаем читабельный и самодокументированный код.
Важные правила:
1) исключения всегда используем для обработки ошибок и внештатных ситуаций, поэтому не используем их для чего-либо еще, чтобы не мозолить глаз;
2) можем случайно «проглотить» исключение с внутреннего уровня вложенности;
3) это дорогое удовольствие — в прикладном программировании крохоборствовать негоже, но и так разбрасываться ресурсами при наличии простых альтернатив нет смысла;
3.1. Единственный выход из одного уровня вложенности:
тривиально заменяется if/break и т.п.
3.2. Несколько выходов из одного уровня вложенности:
3.2.1 Обработка ошибок — только через исключения
(надеюсь, это очевидно; если нет — могу объяснить в отдельной статье)
3.2.2 Перебор вариантов — на примере if:
Проблемы (кроме всегда присущей GOTO неочевидности потока выполнения):
захотели добавить поведение sleep в случаях wannaEat и wannaDance — все, обобшение для wannaEat и wannaDrink разрушено.
Как сделать красиво (сразу расширенный вариант):
3.3. Выход из нескольких уровней вложенности.
3.3.1 Если легко выделить разную логику (разные ответственности):
3.3.2 Сложнее выделить разную логику или при этом усложняется код.
Как правило, это может быть в случае однотипных вложенных циклов:
Вариантов все равно много:
- вынести в отдельную подфункцию с return из внутреннего цикла — самый простой
- обобщить — сделать рекурсивную функцию вида findEqualInArrays(arrayOfArrays, currentArrayIndex, currentFoundItemsArray);
- «if(result) break» — самый топорный:
Это — единственный вариант, который смотрится хуже, чем GOTO, и GOTO даже понятнее. Но практически всегда есть и другие варианты.
Для оставшегося исчезающе малого процента случаев, когда других вариантов нет, нужно просто решить, что все равно можно сделать хотя бы флагами, зато гайдлайны будут проще — «Без GOTO!».
Резюме:
Важнее всего — сопровождаемость.
GOTO всегда ухудшает сопровождаемость, поэтому
нужно обходиться без GOTO — достаточно стандартных средств:
- обработка ошибок через исключения;
- декомпозиция — большой метод, решающий много задач, разбивается на маленькие, решающщие отдельные задачи;
- фиксация логики (вычисленных условий и выражений) в переменных с говорящими названиями.
Источник
GOTO or not GOTO вот в чём вопрос
«Спор возможен там, где истина закрыта. В бесплодных спорах можно бесконечно обсуждать, что в комнате находится закрытой дверью. Но стоит дверь открыть, и ясно станет всем и спорить не о чем, коль каждый истину увидеть сможет»
Статья посвящается Зацепину П.М., выдающемуся инженеру Алтайского государственного университета, под чьим чутким руководством многие студенты, включая автора статьи, постигали магию инженерного творчества.
Введение
Спор о возможности использования в программах оператора GOTO ведётся уже очень давно (официальным его началом признана статья Дейкстры «О вреде оператора GOTO», опубликованная в 1968 году [2]). Через три года мы будем праздновать 50-летний юбилей этого спора. Это хороший повод, чтобы наконец-то «расставить все точки над i» и прекратить спор.
Цитата в эпиграфе выбрана неслучайно. Она в точности отражает текущую ситуацию в споре про GOTO. В нашем случае «комната за закрытой дверью» – это понятная всем постановка задачи. Пока, к сожалению, такой постановки задачи озвучено не было, поэтому споры и не угасают. Противоборствующие стороны спорят хоть и о схожих, но всё-таки о разных вещах, поэтому и не могут найти компромисса.
Давайте займём в этом споре нейтральную сторону, и беспристрастно во всём разберёмся. Рассмотрим доводы «противников» и «защитников» оператора GOTO и решим, «кто из них прав, а кто виноват».
Почему ведутся споры
Как уже было отмечено выше, споры о возможности использования в программах оператора GOTO ведутся из-за отсутствия понятной всем постановки задачи. Грубо говоря, одна из сторон доказывает, что дерево плавает, а другая, что кирпич тонет. Естественно, что при такой постановке каждая из сторон уверена в своей правоте и будет вечно её отстаивать.
Противники GOTO уповают на правила хорошего тона. Именно здесь и спрятан ключ от «закрытой двери», т.к. существуют, по крайней мере, три правила хорошего тона: «хороший тон в структурировании», «хороший тон в быстродействии» и «хороший тон в компактности», но противники GOTO учитывают только один из них.
Защитники GOTO уповают на требования заказчика, где, среди прочих не редко встречаются пункты, связанные с быстродействием и компактностью программы. При такой постановке одним правилом хорошего тона не обойтись – приходится искать компромиссное решение. В результате такого решения в программе иногда и появляется оператор GOTO.
В данном случае кирпич от бревна отличить сложно, т.к. в последнее время при разработке программ быстродействию и компактности уделяется всё меньше внимания. Но это ещё не повод для их полного игнорирования, т.к. на любой спрос должно быть своё предложение.
Озвученная точка зрения весьма поверхностна, т.к. не учитывает деталей спора. Чтобы сформулировать объективную постановку задачи, необходимо рассмотреть аргументы и контраргументы каждой из сторон. Этим мы сейчас и займёмся. Жирные буквы З – это аргументы защитников GOTO, а жирные буквы П – это аргументы противников GOTO.
Доводы «противников» GOTO
З: Если на то пошло, то из языка можно выкинуть практически все операторы.
С точки зрения структурного программирования из языка можно вообще выкинуть все операторы, оставив только while и оператор присваивания. [1] В таком случае программы будут хоть и объёмными, но понятными. Если бы на практике внимание уделялось только структуре программы, то такой шаг был бы обоснованным, но в реальных задачах есть ещё требования на быстродействие и компактность, а этого одним оператором добиться невозможно.
GOTO – признак не кривизны кода, а кривизны языков, в которых без него порой никак (C, C++, C#, Pascal, Java, etc) и кривизны профанации под названием «структурное программирование» с его т.н. «циклами с предусловиями», «циклами с постусловиями» и «ветвлениями», которые являются не элементарными конструкциями, а типовыми паттернами, в которые задача не всегда удобно ложится.
Неудобство заключается в том, что если задача не ложится в эти паттерны идеально, то появляется избыточный код, который в некоторых случаях не удовлетворяет требованиям компактности и быстродействия.
З: Авторитетные мнения достойны внимания, но то, что говорят авторитеты, не есть истина в последней инстанции. Недаром в учёной среде бытует фраза «Если уважаемый учёный говорит, что «это сделать возможно», то он скорее всего прав, а если говорит, что «это сделать невозможно», то скорее всего не прав».
Есть и такие авторитеты, которые высказываются в пользу GOTO, например, Дональд Кнут [4], Фредрик Брукс. [5] Но при решении задачи более целесообразно опираться не на мнение авторитетов, а на здравый смысл.
Доводы «защитников» GOTO
П: В данном случае GOTO структуру программы не портит, но в таком построении нет практической необходимости, т.к. то же самое можно организовать через if/else.
З: Заменить приведённый код на if/else можно только в том случае, если перед завершением не выполняется дополнительных операций.
П: Дополнительные операции можно вынести в отдельную функцию и вызывать её в каждой ветке.
З: Вынос дополнительных операций в отдельную функцию снизит быстродействие программы, а в некоторых случаях это недопустимо.
П: Отдельную функцию можно оформить в виде inline-функции, тогда на быстродействии это никак не скажется.
З: Но тогда программа будет занимать больше памяти. А это тоже в некоторых случаях может противоречить задаче.
В результате этого спора во многих языках были введены процедуры завершения и механизм структурной обработки исключений. Эти инструменты работают немного медленнее GOTO, но более наглядны, поэтому для большинства задач их вполне хватает. Но, опять же, существуют задачи, где критично и это «немного», – в них использование GOTO видится целесообразным.
П: Приведённый код выполняется с той же скоростью и занимает столько же памяти, что и код с GOTO.
З: Данный пример лишний раз показывает, что нужно более внимательно подходить к разработке алгоритма. Использование GOTO в программах допустимо, но не нужно из одной крайности бросаться в другую.
Подведём итоги
Толпы религиозных фанатиков команды квалифицированных программистов часто отказываются от оператора GOTO, даже когда его использование целесообразно с точки зрения эффективности и наглядности программы.
Плохой тон в программировании? Если учитывать только «тон структурирования», то да. Но ведь ещё есть «тон быстродействия» и «тон компактности», поэтому нужно искать компромисс между ними. [6] Программисты «работающие в песочнице», как правило, решают задачи, в которых не приходится задумываться об экономии ресурсов, отсюда и вытекает недопонимание.
Игнорировать «тон структурирования» тоже нельзя, т.к. программу в любом случае придётся дорабатывать, и если в ней будет запутанная структура, то возникнут ненужные затраты. Как и в любых других практических решениях оптимальным является компромиссный вариант: использование GOTO разрешено, но со следующими оговорками:
- Переходить можно только вперёд.
- Заходить в блоки категорически нельзя (либо оставаться, либо выходить).
Скорее всего, байки про вредность оператора GOTO придуманы для начинающих программистов, чтобы они не пользовались им во время обучения. Студенты, изучая в вузах структурное программирование и конструкции языка, знают, что GOTO – это зло. Но когда возникает конкретная задача нужно исходить из неё, а не из шаблонов, которые предоставляет язык программирования. Редко бывает, что практической задаче соответствует конкретный шаблон. GOTO как раз и помогает подогнать существующие шаблоны под решаемую задачу.
Благодарности
Я благодарен людям, которые поделились со мной информацией и своими мыслями по поводу оправданности использования в программах оператора GOTO. Без вашей помощи статья выражала бы однобокое мнение единственного человека, т.е. меня. Вместе с вами нам удалось поддержать конструктивный спор, в результате которого проявился однозначный ответ на тему, волнующую большое число программистов.
Благодарю Дмитрия Леонова за создание сайта bugtraq.ru и за то, что ему удалось сплотить большое количество высококлассных специалистов на своём форуме. Именно на этом форуме развернулась самая интересная дискуссия. Благодарю людей, принявших участие в дискуссии на этом форуме:
Благодарю OlegY, Heller, Zef за примеры кода, где использование GOTO оправдано.
Благодарю HandleX за философские мысли о нужности GOTO при решении практических, а не теоретических задач.
Благодарю amirul за озвучивание правил применения GOTO.
Благодарю AMMOmium за мысль о «байках-страшилках» для начинающих программистов.
Благодарю команду программистов с форума codenet.ru за показательный пример классического спора, а именно следующих лиц: nilbog, koderAlex, OlgaKr, kerdan, kosfiz3A3-968M, IL84, fanto, Sanila_san, nixus, green, newonder.
PS. Спасибо за внимание. Буду рад комментариям; вопросы и возражения также приветствуются.
Источник