11 · Процедуры и функции
Оператор SUB
Вывод отчёта, изменение набора данных, подготовка файла — такие действия часто повторяются в разных местах программы, и их инструкции приходится дублировать. Чтобы этого избежать, действие оформляют как процедуру: SUB объявляет именованный блок инструкций, который выполняется целиком и не возвращает значения.
Объявление и вызов
Объявление начинается ключевым словом SUB, продолжается именем и необязательным списком параметров и завершается END SUB. Тело между этими строками выполняется при вызове.
SUB <имя>(⟦<список параметров>⟧)
<инструкции>
END SUBВызов выполняется отдельной инструкцией: тело проходит от первой инструкции до END SUB или досрочного RETURN и затем возвращает управление вызывающему коду. Прямая форма вызова требует скобок даже при отсутствии аргументов; запись CALL Foo не поддерживается.
<имя>(⟦<список аргументов>⟧)Объявление доступно на верхнем уровне программы и внутри другой процедуры или функции. Объявления обрабатываются до вызовов, поэтому верхнеуровневый вызов может находиться раньше текста объявления. Локальная подпрограмма видна в своём внешнем теле и может захватывать доступные параметры и локальные переменные; внешняя локальная переменная должна быть объявлена раньше текста вложенного объявления. Графический режим не требуется.
Параметры
Список параметров содержит объявления через запятую. Каждый параметр задаёт имя и тип после AS; пустой список допустим. Тип параметра может быть обычным, массивным или функциональным; строковая форма STRING * N в параметрах не поддерживается. Список аргументов содержит выражения в том же порядке, и тип аргумента должен совпадать с типом параметра либо допускать предусмотренное языком преобразование.
<имя параметра> AS <тип параметра>BYVAL <имя параметра> AS <тип параметра>BYREF <имя параметра> AS <тип параметра>BYVAL применяется по умолчанию и передаёт независимую копию значения. Это правило действует для чисел, строк, массивов, TYPE, DICTIONARY и HASHSET. Вложенные изменяемые данные также копируются: изменение элемента массива или коллекции внутри параметра не меняет данные вызывающего кода. Аргумент фиксируется сразу после своего вычисления, до вычисления следующих аргументов. BYREF, напротив, явно связывает параметр с исходной переменной; изменение через такой параметр видно вызывающему коду. Функциональные значения с захватами и внешние ресурсы этим правилом автоматически не дублируются.
BYREF связывает параметр с переменной; массивная переменная записывается с обязательным суффиксом []. Тип должен точно совпадать; преобразование аргумента не допускается. Поле записи, элемент массива и вычисляемое выражение не являются допустимым аргументом BYREF. Передача CONST через BYREF отвергается диагностикой MB2010; для изменяемого аргумента используйте обычную переменную DIM.
Именно BYREF позволяет процедуре изменить переменную вызывающего кода:
SUB AddOne(BYREF value AS INTEGER)
value = value + 1
END SUB
DIM count AS INTEGER = 10
AddOne(count)
PRINT countПрограмма выводит:
11Параметр связан с самой переменной count, поэтому присваивание внутри процедуры изменило её, а не локальную копию. После возврата управления PRINT читает уже новое значение.
Ограничения
Само объявление тело не выполняет: инструкции составляют тело процедуры и выполняются только при вызове. SUB не может быть значением выражения или значением функционального типа, поэтому RETURN внутри процедуры записывается без выражения.
Параметры-массивы
Именованный параметр массива записывается со скобками после имени; AS задаёт тип элемента. Одна запятая внутри суффикса задаёт два измерения. Модификаторы BYVAL и BYREF сохраняют назначение:
<имя параметра>[] AS <тип элемента>BYVAL <имя параметра>[] AS <тип элемента>BYREF <имя параметра>[] AS <тип элемента><имя параметра>[,] AS <тип элемента>В теле подпрограммы и при вызове массивное имя требует [] для передачи массива целиком; индекс выбирает элемент. Например:
SUB ShowBasket(values[] AS STRING)
PRINT values[2]
END SUB
DIM fruits[] AS STRING = SPLIT("яблоко,банан", ",")
ShowBasket(fruits[])Ожидаемый вывод — банан. Скобки вызова ShowBasket круглые, суффикс аргумента fruits — квадратный. BYREF для массива принимает массивную переменную с [] и точно совпадающим типом и рангом; индексированный элемент и поле записи этим правилом не становятся допустимыми аргументами BYREF.
Диагностика
MB2004 означает неверное число аргументов; MB2003 — несовместимый тип; MB2005 — неизвестное вызываемое имя. MB2028 возникает для недопустимого аргумента BYREF. Повторное имя в одной области даёт MB2002; запрещённое перекрытие внешнего имени — MB2016. Избыточная рекурсия даёт MB3011 во время выполнения.
MB2006 означает RETURN со значением внутри SUB. MB2019 запрещает использование процедуры как значения. Запись CALL Foo не поддерживается; применяйте Foo().
См. также
Доступность в модуле
Перед объявлением верхнего уровня можно указать PUBLIC или PRIVATE. Без модификатора имя закрыто внутри файла. Модификатор PUBLIC открывает имя для файлов, которые явно импортируют текущий модуль. Он не меняет остальные правила объявления.
PUBLIC SUB name(parameters)PRIVATE SUB name(parameters)Вложенные объявления не получают модификатора. Подробнее: PUBLIC и PRIVATE и IMPORT.
Группа параметров одного типа
Если несколько соседних параметров имеют одинаковый тип, их имена можно перечислить перед общей частью AS. Например, first, second AS INTEGER равнозначно first AS INTEGER, second AS INTEGER. Явный BYREF или BYVAL перед первым именем действует на всю группу. Для изменения режима отдельного параметра завершите предыдущую группу указанием типа.
Возврат нескольких значений и примеры обращения к ним описаны на странице кортежей.