В списке вечных вопросов ИТ, дать ответ на которые не удаётся чуть ли не с самого начала компьютерной эры, тема порочности оператора GOTO стоит особняком. Всплывающая регулярно на протяжении вот уже без малого шести десятилетий (да есть ли хоть один другой такой пример?!), просто поразительно, какого накала дискуссии она провоцирует и сколько заблуждений связано с ней до сих пор! На днях это случилось в очередной раз на технофоруме Slashdot — и я не удержался от соблазна расставить точки над i.
Если вы когда-нибудь программировали — на любом языке! — то, конечно, в курсе дилеммы. И скорее всего настроены против GOTO. По крайней мере именно такое впечатление о компьютерровской аудитории сложилось у меня за годы работы в журнале: всякий раз, когда я упоминал, что начинал свою программистскую карьеру с бейсика, непременно находились те, кто указывал мне на «изувеченность моих мозгов развратным действием оператора безусловного перехода». Но — куда деваться? Все ранние языки высокого уровня — COBOL, FORTRAN, BASIC и другие — заимствовали безусловный переход из ассемблера. И хоть сохранились воспоминания, что уже в 1959-м году некто Heinz Zemanek (один из отцов языка PL/I) высказывал сомнения в необходимости оператора, только в 1968-м вопрос получил по-настоящему широкую огласку.
Благодарить за это следует Эдсгера Дейкстру и Никлауса Вирта: первый написал короткую статью об опасностях GOTO, второй переименовал её в «GOTO considered harmful» («GOTO небезопасен»), сделав её, как мы сказали бы сейчас, вирусной. Результат? Словосочетание considered harmful стало мемом, а приверженность безусловному переходу вот уже полвека считается признаком дурного вкуса!
Оригинал той статьи, кстати, вот — если только вы в силах переварить оригинального Дейкстру: читается он, откровенно говоря, тяжело. Но если рискнёте, найдёте там немало интересного, начиная прямо с его наблюдения, что «качество программиста обратно пропорционально количеству GOTO в его коде». Называть такой код «похожим на спагетти» стали позже, но в тексте Дейкстры есть ещё как минимум одна примечательная вещь: ссылка на математическое доказательство отсутствия необходимости GOTO, сформулированное Джузеппе Якопини (позже оно было названо теоремой Бёма-Якопини). Суть: машина Тьюринга может быть построена без использования GOTO, только лишь привлечением трёх структур управления: последовательного исполнения, ветвления и циклов.
Кстати, Дейкстра вернулся к вопросу двадцать лет спустя, более ясно сформулировав развратное влияние безусловного перехода на образ мышления программиста. Якобы, вместо того, чтобы продумывать алгоритм, программер поддаётся искушению понатыкать везде GOTO — чем ухудшает читаемость кода и делает программу менее надёжной и предсказуемой.
Казалось бы, этого достаточно, чтобы поставить в споре точку — и создатели многих высокоуровневых языков так и сделали, принципиально проигнорировав GOTO или даже посмеявшись над своими «не слишком далёкими коллегами» (в Python включён модуль с таким названием, выпущенный 1 апреля 2004 года: он работает, да, эмулируя безусловный переход средствами языка, но цель была именно подколоть противников в споре). Вот только и приверженцев GOTO осталось немало — и напрямую, либо эмуляцией, оператор безусловного перехода был реализован во множестве высокоуровневых языков и после Дейкстры.
Вообще-то, внимательно вчитавшись, вы обнаружите, что и сам Дейкстра не против GOTO как такового, он лишь против его неограниченного использования. Однако лучше всех в защиту несчастного оператора выступил великий практик Линус Торвальдс — в сравнительно недавней дискуссии с коллегами по ядру Linux. Вот она — и, в отличие от высокопарного Дейкстры, Торвальдс читается легко.
Смысл его писем: вопреки утвердившемуся мнению, GOTO может делать код более понятным — в частности там, где отсутствие безусловного перехода требует сильного усложнения программы (например, при перехвате ошибок). Далее, вопреки всё тому же мнению, явно или скрыто GOTO присутствует почти во всех языках: проверка с переходом — это GOTO, принудительный выход из цикла — тоже. А виртовский Pascal, на который так любят ссылаться противники GOTO (оператор там изначально отсутствовал, но позже был добавлен, ухудшив читаемость кода), Линус называет проблемой автора языка, повредившегося рассудком, а не проблемой GOTO: язык этот, по мнению Торвальдса, непригоден для практического применения, только для учёбы. Кстати, и «читаемость» в понимании Линуса имеет чисто практический смысл: улучшая её, вы делаете код компактней и проще.
Так что используйте GOTO и не сомневайтесь — как не сомневаются авторы линуксового ядра, в котором десятки тысяч безусловных переходов. И не поддавайтесь попыткам промыть вам мозги теоретиками компьютерных наук, взрощенными на работах мало что понимавших в практике Вирта и Дейкстры!
И вот этого уже совершенно точно хватит, чтобы прекратить спор. Хоть оба участника и излагают свои мысли в весьма радикальной манере (чем «качество программиста» Дейкстры лучше «повреждения мозга» Торвальдса?), они не пытаются сделать их истиной в последней инстанции. Если вчитаться, оба просто ратуют за вдумчивое использование оператора GOTO. А то, что их слова используются для раздувания холивара — вина не Торвальдса или Дейкстры (к тому же давно покойного, так что состязание нечестное), а интернет-комментаторов, жаждущих утвердить своё мнение как единственно правильное. Только посмотрите как начинается та дискуссия с Линусом: приходит некий начинающий разработчик и (буквально!), опираясь всего на один пример, декларирует, что, мол «GOTO плохой, очень плохой!».
Так не повторяйте чужих ошибок, не лейте масла в огонь бесполезного спора. Если уж на то пошло, давно сформулированы типовые случаи, когда GOTO может принести пользу. И помните ещё, что искусство писать красивый код формализуется плохо: один напишет красивую программу даже с GOTO, другому не поможет и его отсутствие. А GOTO может быть полезен в следующих ситуациях:
— Чтобы сделать код более читаемым.
— Чтобы избавиться от дупликации кода.
— Чтобы реализовать таблицу переходов (полезна, например, для создания симуляторов вычислительных систем).
— Чтобы выйти из множественного вложенного цикла.
— Для обработки ошибок.
Пользуйтесь на здоровье!