===== 1.7 Введение в процедуры =====
==== 1. Процедуры ====
**Компонентный Паскаль** поддерживает //процедурный стиль// программирования. Более того, использование процедур //совершенно необходимо//. Как было показано на двух примерах ранее — невозможно заставить исполнить код модуля, кроме как обратиться к имени процедуры. Идея процедуры используется почти во всех парадигмах программирования: **ООП, функциональный стиль, АОП**... Пожалуй, единственный стиль, который не использует процедурный подход — //макаронное программирование//((это были ужасные времена. Писать в таком стиле это просто вынос мозга. Подробнее почитать можно в [[https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B0%D0%B3%D0%B5%D1%82%D1%82%D0%B8-%D0%BA%D0%BE%D0%B4|Википедии]])). Наличие такой возможности во всех парадигмах только ещё раз подчёркивает её важность.
==== 2. Оформление процедуры ====
Процедура в **КП** (впрочем, как и в любом **ЯП**) — это маленькая программа, работающая в интересах всей программы, причём, обычно, когда процедура получает управление, основная программа ждёт результатов её работы((Основная программа на современных компьютерах может не ждать, пока подпрограмма выполняется (если это требует много времени). Если такая подпрограмма выполняется в потоке главной программы она называется //сопрограмма// или //легковесный поток// (при этом может останавливать выполнение основной программы). Если в состав компьютера входит множество вычислительных узлов, то такая подпрограмма называется //процесс//.Подробнее смотрите в [[https://ru.wikipedia.org/wiki/Green_threads|Википедии]])). Поэтому нет ничего удивительного в том, что оформление процедуры очень похоже на оформление модуля:
PROCEDURE Старт;
BEGIN
мЛог.String('Привет, мир!');
мЛог.Ln
END Старт;
Это та самая процедура, которая уже встречалась в примере "Привет, мир!".
Процедура объявляется ключевым словом ''PROCEDURE'', после которого следует само имя процедуры, заканчивается объявление процедуры — точкой с запятой. Как было описано раньше, по соглашению принятому в **КП** — имена процедур начинаются с большой буквы.
Начинается любая процедура с ключевого слова ''BEGIN'', а заканчивается ключевым словом ''END''. Причём после ''END'' обязательно указывается имя процедуры. Так компилятор может контролировать содержимое процедуры. После имени процедуры следует точка с запятой, которая говорит компилятору о том, что это ещё не конец модуля (только ''END'' с точкой может быть указателем того, что модуль закончился).
Весь полезный код начинается после ключевого слова ''BEGIN''. Ключевое слово ''VAR'' также может быть использовано за объявлением процедуры, в этой секции описываются переменные, которые могут быть использованы только в текущей процедуре, но не могут быть использованы во всём модуле. В отличии от модуля использовать ключевое слово ''IMPORT'' в процедурах запрещается. Ключевое слово ''CONST'' вполне употребимо, и компилятор не будет возражать.
Как было описано в одном из предыдущих разделов, после последней инструкции в теле процедуры — перед ''END'' — точку с запятой ставить не нужно.
==== 3. Процедура без параметров ====
Параметром называется какая-либо переменная, которая передаётся в процедуру. Процедура, которая определена без параметров (например, ''Старт'') называется "процедурой без параметров".
Такие процедуры встречаются не часто. И использование таких процедур хоть и возможно в **КП**, но не приветствуется. Как правило такие процедуры используют переменные, объявленные //в других местах//. Изменение этих переменных таким способом //нарушает// принцип //разделения и сокрытия информации//, что приводит к //снижению// надёжности программ.
==== 4. Процедура с параметрами ====
Это основной вид процедур в **КП**. Для того, чтобы процедура приняла какие либо параметры, нужно их описать в объявлении процедуры:
В приведённой процедуре ''LogReal'' используется формальный параметр ''r''. Тип этого параметра — ''REAL''. Далее, те параметры, которые процедура требует на входе, вместо //формальные параметры// (как официально принято) будем их называть //входные параметры//, или совсем коротко //параметры//((Отказ от официального названия //формальные параметры// в пользу //входные параметры//, или просто //параметры// продиктован из-за схожести понятий //формальные параметры// и //фактические параметры//, что не очень приятно на слух. Подробнее можно ознакомиться в Википедии.)). В объявлениях процедур можно описывать несколько параметров, все они записываются в скобках подряд и разделяются точкой с запятой.
==== 5. Вызов процедуры с параметрами ====
Вызов процедуры с параметрами можно выполнить передав процедуре фактические параметры (официальное название). Далее такие параметры будем называть //передаваемые аргументы// или совсем коротко //аргументы//((Неофициальные названия //формальных параметров// и //фактических параметров// вводятся целенаправленно. Различать //параметры// и //аргументы// на слух значительно легче.)) Полный код программы с вызовом процедуры представлен ниже:
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'' можно писать только одну, что приводит к сокращению набираемого кода. В этом примере заключается одна из ключевых идей процедурного стиля. Но обратите внимание, что для вызова процедуры компьютеру требуется выполнить ряд дополнительный служебных действий, и при возврате из процедуры также выполняются некоторые действия, что приводит к дополнительным небольшим затратам времени. Поэтому, //вызов процедуры должен быть оправдан//. Само тело процедуры не должно состоять из одной инструкции, так как в этом //нет никакого смысла//.
Ещё один момент на который необходимо обратить внимание — это порядок размещения процедур. Если процедуру ''Вещ Печать'' разместить позже процедуры ''Старт'' компилятор **КП** не сможет понять, что за процедуру вызывает инструкция ''Вещ Печать(вПерем)''. Компиляция завершится ошибкой. Поэтому, объявление процедур должно быть упреждающим.
==== 6. Возврат значения из процедуры ====
Процедура может возвращать какое-либо значение в случае необходимости. Для этого существует специальное ключевое слово ''RETURN''. После него размещается переменная, значение которой должно быть возвращено. Вместо переменной можно написать какое-либо выражение (например, вызов другой процедуры)((Вообще, в других языках программирования (например, [[https://ru.wikipedia.org/wiki/Visual_Basic|Visual Basic]]) процедуры возвращающие результат называются функции. И для определения функций предусмотрено отдельное ключевое слово. Но с точки зрения современного подхода это является неоправданным излишеством (функции из текста программы вызываются также, как и процедуры, отличить можно только по факту возврата результата).)). Давайте модифицируем процедуру ''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'' (мы тип этой константы не описали). Кроме того, ни модуль, ни процедура ''Старт'' не подозревают о наличии переменной ''вРезульт'' внутри процедуры ''Вещ Вычисл''. А значит ни одна процедура модуля (и сам модуль) не могут испортить её значение. Так достигается сокрытие информации.
==== 7. Экспорт процедуры ====
Самые внимательные читатели уже обратили внимание на то, что за именем процедуры ''Старт'' стоит символ //звёздочка//. Этот символ как раз и указывает компилятору сделать //экспорт// процедуры из модуля. Попробуйте уберите звёздочку и скомпилируйте — вы не сможете вызвать процедуру ''Старт'' . После экспорта, указанная процедура может быть вызвана извне модуля. Именно поэтому её можно запустить на выполнение через ''КОММАНДЕР''. Тема экспорта //очень важна//, и в этом разделе получила лишь минимальное освещение, о других особенностях экспорта речь ещё пойдёт дальше.
==== 8. Заключение ====
В этой части учебника были рассмотрены в первом приближении очень важные положения **КП** процедурного стиля:
- Как объявить процедуру
- Как объявить входные параметры процедуры
- Как вызвать процедуру
- Как заставить процедуру возвращать результат
- Как сделать процедуру доступной для исполнения извне модуля
Этих знаний должно хватить для написания программ в //хорошем// процедурном стиле. Описанные здесь особенности лишь //часть// того, что можно сделать с процедурами, но так как полное освещение потребует более глубоких знаний в этой области, пока остановимся на этом.