Компонентный Паскаль поддерживает процедурный стиль программирования. Более того, использование процедур совершенно необходимо. Как было показано на двух примерах ранее — невозможно заставить исполнить код модуля, кроме как обратиться к имени процедуры. Идея процедуры используется почти во всех парадигмах программирования: ООП, функциональный стиль, АОП… Пожалуй, единственный стиль, который не использует процедурный подход — макаронное программирование1). Наличие такой возможности во всех парадигмах только ещё раз подчёркивает её важность.
Процедура в КП (впрочем, как и в любом ЯП) — это маленькая программа, работающая в интересах всей программы, причём, обычно, когда процедура получает управление, основная программа ждёт результатов её работы2). Поэтому нет ничего удивительного в том, что оформление процедуры очень похоже на оформление модуля:
PROCEDURE Старт; BEGIN мЛог.String('Привет, мир!'); мЛог.Ln END Старт;
Это та самая процедура, которая уже встречалась в примере «Привет, мир!».
Процедура объявляется ключевым словом PROCEDURE
, после которого следует само имя процедуры, заканчивается объявление процедуры — точкой с запятой. Как было описано раньше, по соглашению принятому в КП — имена процедур начинаются с большой буквы.
Начинается любая процедура с ключевого слова BEGIN
, а заканчивается ключевым словом END
. Причём после END
обязательно указывается имя процедуры. Так компилятор может контролировать содержимое процедуры. После имени процедуры следует точка с запятой, которая говорит компилятору о том, что это ещё не конец модуля (только END
с точкой может быть указателем того, что модуль закончился).
Весь полезный код начинается после ключевого слова BEGIN
. Ключевое слово VAR
также может быть использовано за объявлением процедуры, в этой секции описываются переменные, которые могут быть использованы только в текущей процедуре, но не могут быть использованы во всём модуле. В отличии от модуля использовать ключевое слово IMPORT
в процедурах запрещается. Ключевое слово CONST
вполне употребимо, и компилятор не будет возражать.
Как было описано в одном из предыдущих разделов, после последней инструкции в теле процедуры — перед END
— точку с запятой ставить не нужно.
Параметром называется какая-либо переменная, которая передаётся в процедуру. Процедура, которая определена без параметров (например, Старт
) называется «процедурой без параметров».
Такие процедуры встречаются не часто. И использование таких процедур хоть и возможно в КП, но не приветствуется. Как правило такие процедуры используют переменные, объявленные в других местах. Изменение этих переменных таким способом нарушает принцип разделения и сокрытия информации, что приводит к снижению надёжности программ.
Это основной вид процедур в КП. Для того, чтобы процедура приняла какие либо параметры, нужно их описать в объявлении процедуры:
В приведённой процедуре LogReal
используется формальный параметр r
. Тип этого параметра — REAL
. Далее, те параметры, которые процедура требует на входе, вместо формальные параметры (как официально принято) будем их называть входные параметры, или совсем коротко параметры3). В объявлениях процедур можно описывать несколько параметров, все они записываются в скобках подряд и разделяются точкой с запятой.
Вызов процедуры с параметрами можно выполнить передав процедуре фактические параметры (официальное название). Далее такие параметры будем называть передаваемые аргументы или совсем коротко аргументы4) Полный код программы с вызовом процедуры представлен ниже:
Hello03.odc
MODULE КнигаПривет3; (* это третья программа на языке Компонентный Паскаль. Она выполняет кое-какие математические операции и показывает как использовать процедуры *) IMPORT мЛог := Log; CONST _конст = 2; VAR цПерем: INTEGER; вПерем: REAL; PROCEDURE Вещ_Печать (пвПерем: REAL); VAR BEGIN мЛог.Real(пвПерем); мЛог.Ln END Вещ_Печать; PROCEDURE Старт*; VAR BEGIN цПерем := 3; вПерем := _конст / цПерем; мЛог.String('Привет, мир!'); мЛог.Ln; Вещ_Печать(вПерем) END Старт; BEGIN END КнигаПривет3.
Этот вариант программы делает всё тоже самое, что и предыдущая, но с использованием дополнительной процедуры Вещ_ Печать
описанной выше. Передаваемый аргумент вПерем
должен иметь тот же тип, что и требуемый параметр в объявлении процедуры. В противном случае, КП посчитает это ошибкой. Теперь вместо двух инструкций мЛог.Real(вПерем); Log.Ln
можно писать только одну, что приводит к сокращению набираемого кода. В этом примере заключается одна из ключевых идей процедурного стиля. Но обратите внимание, что для вызова процедуры компьютеру требуется выполнить ряд дополнительный служебных действий, и при возврате из процедуры также выполняются некоторые действия, что приводит к дополнительным небольшим затратам времени. Поэтому, вызов процедуры должен быть оправдан. Само тело процедуры не должно состоять из одной инструкции, так как в этом нет никакого смысла.
Ещё один момент на который необходимо обратить внимание — это порядок размещения процедур. Если процедуру Вещ Печать
разместить позже процедуры Старт
компилятор КП не сможет понять, что за процедуру вызывает инструкция Вещ Печать(вПерем)
. Компиляция завершится ошибкой. Поэтому, объявление процедур должно быть упреждающим.
Процедура может возвращать какое-либо значение в случае необходимости. Для этого существует специальное ключевое слово RETURN
. После него размещается переменная, значение которой должно быть возвращено. Вместо переменной можно написать какое-либо выражение (например, вызов другой процедуры)5). Давайте модифицируем процедуру LogReal
, чтобы она это могла делать:
PROCEDURE Вещ_Вычисл(пцЗнач1, пцЗнач2: INTEGER): REAL; VAR вРезульт: REAL; BEGIN вРезульт := пцЗнач1 / пцЗнач2; мЛог.Real(вРезульт); мЛог.Ln; RETURN вРезульт END Вещ_Вычисл;
Теперь процедура Вещ Печать
не только выводит значение переменной типа REAL
, но и может рассчитать его. Поэтому, было изменено название процедуры на Вещ Вычисл
. Обратите внимание, как определены два входных параметра типа INTEGER
— подряд через запятую. За списком параметров указан тип возвращаемого значения. Также впервые была использована секция процедуры VAR
, в которой определена переменная вРезульт
типа REAL
, значение которой, рассчитывается внутри процедуры, и в конечном итоге возвращается. Если ключевое слово RETURN
разместить перед вычислениями, то сами вычисления не будут выполнены, а в процедуру Старт
будет возвращено непонятно что (мусор).
Полный код модифицированной программы приведён ниже:
Hello04.odc
MODULE КнигаПривет4; (* это четвёртая программа на языке Компонентный Паскаль. Она выполняет кое-какие математические операции и показывает как использовать процедуры, которые возвращают значение *) IMPORT мЛог := Log, Math; CONST _конст = 2; VAR цПерем: INTEGER; вПерем: REAL; PROCEDURE Вещ_Вычисл (пцЗнач1, пцЗнач2: INTEGER): REAL; VAR вРезульт: REAL; BEGIN вРезульт := пцЗнач1 / пцЗнач2; мЛог.Real(вРезульт); мЛог.Ln; RETURN вРезульт END Вещ_Вычисл; PROCEDURE Старт*; VAR BEGIN мЛог.String('Привет, мир!'); мЛог.Ln; цПерем := 3; вПерем := Вещ_Вычисл(_конст, цПерем) END Старт; BEGIN END КнигаПривет4.
Как видно из текста модуля, процедура Старт
стала существенно короче. Кроме того, константа _конст
успешно передаётся в процедуру. Это говорит о том, что компилятор КП автоматически присвоил ей тип INTEGER
(мы тип этой константы не описали). Кроме того, ни модуль, ни процедура Старт
не подозревают о наличии переменной вРезульт
внутри процедуры Вещ Вычисл
. А значит ни одна процедура модуля (и сам модуль) не могут испортить её значение. Так достигается сокрытие информации.
Самые внимательные читатели уже обратили внимание на то, что за именем процедуры Старт
стоит символ звёздочка. Этот символ как раз и указывает компилятору сделать экспорт процедуры из модуля. Попробуйте уберите звёздочку и скомпилируйте — вы не сможете вызвать процедуру Старт
. После экспорта, указанная процедура может быть вызвана извне модуля. Именно поэтому её можно запустить на выполнение через КОММАНДЕР
. Тема экспорта очень важна, и в этом разделе получила лишь минимальное освещение, о других особенностях экспорта речь ещё пойдёт дальше.
В этой части учебника были рассмотрены в первом приближении очень важные положения КП процедурного стиля:
Этих знаний должно хватить для написания программ в хорошем процедурном стиле. Описанные здесь особенности лишь часть того, что можно сделать с процедурами, но так как полное освещение потребует более глубоких знаний в этой области, пока остановимся на этом.