Оператор GOTO в 1С — табу или волшебная палочка?
В ежедневной практике программирования на платформе 1С оператор Goto (Перейти) практически не встречается. Не удивительно, что половина специалистов даже не подозревает о существовании данного оператора. Ну, а знающим он внушает ужас, и его стараются избегать. Так что же это за редкий оператор, и для чего он нужен?
- Описание
- Подробнее
Описание
Что такое оператор GOTO?
Оператор GOTO — это базовый оператор для управления потоком выполнения кода. Обычно платформа выполняет код в рамках единого блока (модуля, процедура, тело цикла) по очереди — выполнив одну команду, переходит к выполнению следующей. Но если платформа 1С видит оператор GOTO, то после него начинает выполняться строчка в произвольном месте кода, на которую ссылается этот оператор.
Указание новой точки выполнения кода происходит с помощью Меток, численно-буквенных идентификаторов, которые начинаются на символ ~ (тильда). Требования к идентификатору Метки не столь строги, как к прочим идентификаторам, и допускается делать Метки начиная с числа или даже вовсе без букв. Чтобы выполнить пометку строки, перед ней необходимо указать Метку и двоеточие. При вызове оператора GOTO вместе с ним указывается Метка и точка с запятой.
Пока Истина Цикл
Перейти ~КонецЦикла; // выходим из цикла
Сообщить("Застрял в бесконечном цикле..."); // никогда не выполнится
КонецЦикла;
~КонецЦикла:
Сообщить("Вышел из цикла!");Переходы ограничены пределами текущих областей видимости — функциями, процедурами и модулями объектов, в которых применяется оператор GOTO. Т.е. у меток для использования переходов есть только пределы локальной видимости (напомню, что, в отличии от многих языков программирования высоких уровней, в 1С для тела Условий и Циклов нет собственной вложенной области видимости).
С появлением 8.2 появилось новое ограничение — запрещено использовать GOTO в коде управляемых приложений, которые должны выполняться на стороне клиента. Ограничение связано с отсутствием реализации оператора в веб-клиенте.
Почему у оператора Goto плохая слава в академической среде?
Изначально в низкоуровневых языках программирования операторы условных и безусловных переходов были единственно доступными операторами управления потоком выполнения. Но с появлением языков программирования высокого уровня появились новые альтернативные и более удобные операторы — Условия и Циклы, с которыми возникла парадигма Структурного Программирования.
Программисты "старой школы", которые уже имели за плечами огромный багаж программирования в машинных кодах и на версиях Ассемблера, с радостью приняли новый "синтаксический сахар", но при этом не отказались от некоторых ранее наработанных алгоритмов на переходах. Использование "не структурных операторов управления" затрудняло понимание и поддержку такого кода молодым поколением программистов, которые никогда ранее не работали с языками низкого уровня. Но было намного хуже, когда эти молодые специалисты видя операторы GOTO у старших товарищей, начинали за ними повторять и активно использовать переходы в своем коде, что должно было продемонстрировать "крутизну", но в результате только превращали их программы в трудно читаемых "монстров" со множеством ошибок. Такой код получил нарицательное прозвище "спагетти-код".
В 1966 году было опубликовано "Доказательство Бема-Якопини", суть которого заключалась в том, что любой алгоритм можно переписать без переходов по меткам на одних только условиях и циклах, хоть это может приводить к существенным потерям в эффективности. В 1968 году голландский ученый Эдсгер Дейкстра, будучи уже известным в ИТ-сообществе после своей публикации алгоритма для поиска кратчайшего пути на графе и после участия в группе разработки компилятора языка Algol, публикует свою статью "О вреде оператора GOTO" (GOTO considered harmful), которая вызвала бурные академические споры. Дейкстра утверждал, что на основе статистического анализа пришел к выводу, что количество ошибок в изученном программном коде прямо пропорционально количеству использованных в тексте программ GOTO и потому предложил радикально улучшить качество программирования введением запрета на данный оператор.
С этого момента начались гонения на оператор GOTO. И запреты на использование оператора часто были настолько фанатично слепы, что за него пришлось публично вступиться таким известным людям как Дональду Кнуту, Брайану Кернигану и Деннису Ритчи, Линусу Торвальдсу, Стиву МакКоннелли и многим другим чуть менее известным. Эти авторитетные люди заявили, что оператор GOTO улучшает скорость, размер и ясность кода программы, но только при разумном использовании разумным программистом.
Любопытные факты. Общеизвестно, что Дейкстра был мало заинтересован в приеме на старшие курсы университета, где он преподавал, студентов со знанием Фортрана по той причине, что вместе с этими знаниями могли привиться дурные привычки программирования. Под дурной привычкой имелось в виду, конечно же, применение GOTO, правила использования которого в Фортране было очень свободным. В результате давления порицанием от научного сообщества в 1978 году в Фортране сначала запретили свободный вход/выход в рамках циклов (до сих пор разрешен в C++), а потом в 1995 запретили почти все прочие варианты переходов.
Откуда у оператора GOTO плохая слава в 1С?
Почему с оператором GOTO все не так однозначно в универсальных высокоуровневых языках, на которых до сих пор делают ассемблерные вставки, более-менее понятно. Но почему в сообществе 1С при виде использования оператора перехода начинается настоящая истерика с криками "четвертовать", "сжечь", "повесить", "волчий билет и запрет на программирование"?
Откуда столько негатива? Ведь язык 1С — это DSL, который не предполагает написание сложных алгоритмов, а потому большая часть программистов пишет простые скрипты и скорее всего даже не догадывается про существование GOTO, а остальные просто помнят о его существовании, но при этом превосходно обходятся без него.
Причина №1 — Академический навык. Далеко не все программисты 1С самоучки или свитчеры из финансистов. Есть те, кто окончили кафедры компьютерных наук и из-за событий непреодолимой силы попали в 1С. А гонения на оператор GOTO — это классика современных компьютерных наук. Таким людям на лекциях сказали, что использование GOTO — это плохо. Если они на экзамене смогли воспроизвести данное утверждение, то получали хорошую оценку и закрепляли навык порицания.
Причина №2 — Портирование внешнего кода. Часто при работе с данными в бинарных форматах, драйверами торгового оборудования и с прочими библиотеками, от поставщика есть пример кода, который легко скопировать в 1С и который после этого даже будет работать. Но ради эффективности такой код часто низкоуровневый и содержит множественные GOTO. Если потом неподготовленному человеку потребуется что-то там исправить, то возникает состояние "плачу кровавыми слезами" и кредо "никогда в жизни не буду связываться с GOTO".
Причина №3 — Обфускация кода. Когда хотят затруднить самостоятельное использование кода клиентом и обеспечить дополнительный доход для поддержки, то код запутывают. Есть несколько вариантов запутывания на уровнях исходного кода и байткода, но практически все варианты для затруднения анализа используют множественные GOTO. Когда такой код видят специалисты клиента, то он вызывает высокий уровень раздражения. Когда такой запутанный код работает с ошибками, которые нужно исправить, а "поставщик" исчез, то раздражение возрастает на порядок.
Любопытные факты. На уровне байткода не существуют никаких структурных операторов, а все условия, циклы, обработки ошибок и процедуры с функциями реализованы на условных и безусловных переходах. Т.е. байткод 1С сильно подобен ассемблеру. Выходит, что изначально для платформы 1С оператор GOTO является "родным", а циклы и условия по своей сути являются "синтаксическим сахаром" для удобства программистов.
Когда можно и нужно использовать GOTO в 1С?
Предыдущий раздел дает часть ответа. Так если в портируемом алгоритме были GOTO, то проще их сохранить, а не терять несколько дней на рефакторинг с риском привнесения ошибок.
Так же без GOTO никуда, если вы решили писать собственную версию обфускатора. Но не рекомендую таким заниматься. Во первых, ниша уже занята и у вас практически нет шансов совершить революцию, чтобы потеснить конкурентов и хотя бы отбить затраты на разработку (даже для себя проще купить чужое решение, чем тратить время на разработку). А во вторых, тех, кто обфусцирует собственный код, никто не любит — это может существенно испортить деловую репутацию (кто занимается отраслевыми решениями меня поймут).
Но все же, а какие адекватные причины могут побудить использовать GOTO при написании своего кода? Предлагаю следующий перечень по возрастанию степени ответственности за принятое решение:
1. Сложный входящий алгоритм
Не всегда мы придумываем свои алгоритмы самостоятельно, иногда нам ставят задачу просто кодировать согласно чужим "блок-схемам". Это может быть как адаптация "под 1С" чужого кода на универсальном языке программирования. Или это может постановка от пользователя, который именно "так" видит решение. И эти внешние алгоритмы могут содержать переходы в произвольные части кода, которые можно реализовать исключительно с помощью GOTO. Что-то такое:

Только на схеме маленький и простой вариант, который легко переделать (о переделке еще вспомним в пункте №3). Но представьте, что к вам пришел руководитель кадровой службы и попросил сделать для него мегакрутую штатную расстановку, на которой будет ВСЁ, и в описании которой десятки таких странных "поворотов сюжета".
Вы можете заявить, что такой алгоритм стоит предварительно "причесать" — структурировать, выровнять линии управления, возможно упростить некоторые условия согласно карт Карно. Но хватит ли у вас квалификации и знания предметной области для написания своего алгоритма? А если вам платят только за программирование, то готовы ли несколько вечеров забрать у своей семьи и жить на работе? А как будете поддерживать написанное? А если к вам снова придет заказчик и скажет — "тут ещё работы на 5 минут", просто добавь в этом блоке новое условие под "новый вчера принятый закон" и утром уже должно быть в "проде" или иначе компанию ждут гигантские штрафы, но вы изначально не учли вероятность изменения законов и для вашего нового алгоритма теперь нужна неделя на "всё переписать"?
Если ответственность за создание алгоритма на другом человеке, то чаще лучше не требовать её на себя и не привносить лишние риски на свою "пятую точку". В конце-концов с нами тоже работают профессионалы и их алгоритмы могут являться не результатом безумия, а компромиссом между "понятностью" и "внешними условиями".
2. Быстрый выход из вложенных циклов
Мало кто знает, но операторы Прервать (Break) и Продолжить (Continue) — это тоже операторы безусловного перехода, подобные GOTO. Первый совершает переход на скрытую метку после цикла, а второй на окончание цикла перед переходом на следующую итерацию. Для нас это чрезвычайно удобные переходы уже как минимум за счет того, что не нужно явно ставить метки и следить за уникальностью их имен — все происходит автоматически. Но не всегда циклы одноуровневые!
Не очень часто, но все же время от времени нужно писать циклы, где уровней очень много — например, обработка данных с группировкой по периодам, видам контрагентов, самим контрагентам, валютам, договорам, заказам и т.д. И далее предположим, что для нашей бизнес-задачи погашения долгов нужно отобрать из этой древовидной структуры данные на определенную сумму, но с хитрым условием, которое прописали юристы в договоре — нужно возвращать деньги за полные поставки с сортировками сначала по дате заключения заказа, а потом по дате фактического прибытия груза. Лимит по сумме тоже хитрый — поскольку и мы и поставщики транснациональные компании, то долги нужно возвращать в разных валютах и при этом действуют независимые лимиты на уровне валюты (ограничения от банков) и в целом по управленческой валюте (ограничение от своих финансистов).
Как обычно делают такие "прыжки" через несколько уровней цикла? На каждом уровне создаются сигнальные переменные, значение которых проверяем при завершении тела цикла. Если значение переменной изменилось, то прерываем выполнение текущего уровня и переходим к проверке значений переменных выше. Но чем больше уровней, чем сложнее будет логика "возврата" — слишком много таких переменных и блоков кода по их проверке.
