IF ... ELSE ... END-IF

Как COBOL-программа выбирает, что делать дальше — и почему сравнивать строки здесь не так просто, как кажется.

IF — конструкция, которая проверяет условие и выполняет один из двух блоков кода в зависимости от результата, а END-IF — обязательная точка, где эта конструкция заканчивается.

Зачем в языке вообще нужен IF

Любая программа рано или поздно должна на что-то реагировать по-разному. Клиенту банка начислить проценты, если у него на счету больше определённой суммы, и не начислять, если денег меньше. Заказ пометить как «просроченный», если дата доставки прошла, и оставить в работе, если ещё нет. Без условной конструкции программа могла бы делать только одно и то же для всех данных подряд — а это бесполезно почти для любой реальной задачи.

В COBOL за это отвечает конструкция IF ... ELSE ... END-IF. Она появилась в языке ещё в конце 1950-х и с тех пор почти не менялась — те же банковские системы, которые писались на COBOL полвека назад, используют ровно этот же синтаксис сегодня.

Синтаксис и разбор по строкам

Вот минимальный пример: проверка, хватает ли денег на счету клиента для списания.

       IF WS-BALANCE >= WS-WITHDRAW-AMOUNT
           SUBTRACT WS-WITHDRAW-AMOUNT FROM WS-BALANCE
           DISPLAY "Списание выполнено"
       ELSE
           DISPLAY "Недостаточно средств"
       END-IF.

Разберём построчно:

  • IF WS-BALANCE >= WS-WITHDRAW-AMOUNT — условие. WS-BALANCE и WS-WITHDRAW-AMOUNT — это переменные (в COBOL их принято называть «поля» или «элементы данных»), а >= значит «больше или равно», совсем как в математике.
  • Следующие две строки выполняются, только если условие верно — это ветка «истина».
  • ELSE — начало ветки «ложь». Она необязательна: можно написать IF без ELSE, и тогда при ложном условии программа просто ничего не сделает и пойдёт дальше.
  • END-IF — явный конец конструкции. Это отдельная особенность COBOL: в большинстве современных языков границы IF задают фигурные скобки или отступы, а здесь нужно явное слово-терминатор.

Отступы в примере — это просто договорённость программистов для читаемости, сам компилятор на них не смотрит (в отличие, например, от Python). Но точка в самом конце обязательна — она завершает весь оператор целиком.

Почему END-IF обязателен

В очень старом COBOL (до стандарта 1985 года) специального END-IF не было вообще — границу условия обозначала точка в конце. Проблема в том, что если внутри IF несколько инструкций, точка ставилась только один раз, в самом конце всей конструкции — и было легко случайно поставить её раньше, оборвав условие на середине. Это одна из классических ошибок, из-за которых старый банковский код COBOL иногда вёл себя не так, как задумывалось: часть кода, которая должна была быть «внутри» IF, на самом деле выполнялась всегда.

Явный END-IF решает эту проблему — граница видна сразу, и её нельзя перепутать. В современном коде END-IF пишут почти всегда, даже когда без него можно было бы обойтись.

Сравнение чисел и строк — в чём разница

С числами всё интуитивно: >, <, =, >=, <= работают как в математике. Но COBOL исторически — язык обработки текстовых полей фиксированной длины (вспомните перфокарты и построчные файлы), и сравнение строк устроено иначе, чем в Python или JavaScript.

Допустим, есть два поля: WS-NAME-A длиной 10 символов со значением "IVAN" и WS-NAME-B длиной 10 символов со значением "IVAN ". На первый взгляд это разные строки — во втором случае явно есть лишний пробел. Но в COBOL оба поля физически хранят ровно 10 символов, и короткое значение "IVAN" автоматически дополняется пробелами справа до полной длины поля. Поэтому:

       IF WS-NAME-A = WS-NAME-B
           DISPLAY "Имена совпадают"
       END-IF.

Вывод:

Имена совпадают

Сравнение вернёт «истина» — потому что оба поля после дополнения пробелами выглядят одинаково: "IVAN ". Это удобно, когда данные приходят из фиксированных файлов, но может удивить, если вы ждёте поведения как в языках, где строки сравниваются посимвольно без выравнивания. Ещё один нюанс: если сравнивать поля разной длины, более короткое тоже мысленно «дополняется» пробелами до длины более длинного перед сравнением — само поле в памяти при этом не меняется.

Мини-пример: классификация возраста

Более развёрнутый пример — определить возрастную категорию для анкеты.

       IF WS-AGE < 18
           MOVE "Несовершеннолетний" TO WS-CATEGORY
       ELSE
           IF WS-AGE < 65
               MOVE "Взрослый" TO WS-CATEGORY
           ELSE
               MOVE "Пенсионный возраст" TO WS-CATEGORY
           END-IF
       END-IF.

       DISPLAY "Категория: " WS-CATEGORY.

Вывод (при WS-AGE = 40):

Категория: Взрослый

Здесь один IF вложен в ELSE-ветку другого — это стандартный способ проверить несколько вариантов подряд, аналог цепочки else if в других языках. Каждый вложенный IF требует своего END-IF, поэтому у конструкций с несколькими уровнями вложенности легко потерять счёт скобкам-терминаторам, если не следить за отступами.

Частые ошибки

  • Забыть END-IF в многострочном блоке. Если внутри IF несколько операторов и нет END-IF, компилятор либо выдаст ошибку, либо (в старом стиле с точкой) свяжет границу условия не с той строкой, что задумывалось.
  • Ждать «умного» сравнения строк, как в Python. Помните: COBOL сравнивает поля фиксированной длины с выравниванием пробелами, а не «содержимое» в привычном смысле — лишние пробелы в середине строки, а не в конце, всё равно будут учтены.
  • Путать = с присваиванием. В COBOL присваивание — это отдельный оператор MOVE значение TO поле, а = внутри IF — только сравнение. Смешать их синтаксически нельзя, но концептуально этой разницы иногда не хватает тем, кто пришёл из языков, где = используется для обоих действий.

Итоги

  • IF...ELSE...END-IF — основная условная конструкция COBOL, END-IF обязателен для явной границы блока.
  • ELSE необязателен, если нужно действие только при истинном условии.
  • Числа сравниваются как обычно, а строковые поля COBOL сравнивает с учётом дополнения пробелами до фиксированной длины поля.
  • Вложенные IF внутри ELSE — стандартный способ проверить несколько условий по очереди.
Проверьте себя
1. Что обязательно должно завершать многострочную конструкцию IF в современном COBOL?
AКлючевое слово END-IF
BФигурная скобка }
CПросто отступ следующей строки
DНичего, IF не нужно завершать
2. Поле WS-A (длина 5) содержит "АВ", поле WS-B (длина 5) содержит "АВ " (АВ и три пробела). Что вернёт IF WS-A = WS-B?
AИстина, потому что оба поля физически хранят "АВ" плюс пробелы до полной длины
BЛожь, потому что видимое количество символов разное
CОшибку компиляции
DИстина только если оба поля объявлены с одинаковым типом PIC 9