9. Процедуры и функции

Процедуры и функции представляют собой относительно самостоятельные фрагменты программы, оформленные особым образом и снабженные именем. Упоминание этого имени в тексте программы называется вызовом процедуры (функции). Отличие функции от процедуры заключается в том, что результатом исполнения операторов, образующих тело функции, всегда является некоторое значение, поэтому обращение к функции можно использовать в соответствующих выражениях наряду с переменными и константами. Обобщенно процедуры и функции будем называть подпрограммами.

Подпрограммы делятся на встроенные и пользовательские. Встроенные подпрограммы входят в состав API DataExpress, пользовательские определяются разработчиком в скрипте. Синтаксис определения процедуры и функции:

// Определение процедуры
procedure MyProc([список параметров]);
beginend;
// Определение функции
function MyFunc([список параметров]): [тип данных];
beginend;

Верхняя строка процедуры называется заголовком процедуры и состоит из служебного слова procedure, названия функции, списка параметров и точки с запятой. Следом идут операторные скобки begin-end, называемые телом процедуры. После end обязательно ставится точка с запятой. Процедура может не иметь параметров, в этом случае ставить скобки необязательно.

Заголовок функции отличается тем, что вместо слова procedure пишется function, а после списка параметров или имени (если нет параметров) идет двоеточие и тип результата, который возвращает функция. Вызов встроенной или пользовательской подпрограммы имеет следующий синтаксис:

Proc([список параметров]);
MyFunc([список параметров]);
x := a + MyFunc([список параметров]) – b …
y := MyFunc2 + 1000 - b;

Если подпрограмма не имеет параметров, то скобки необязательны. Имя подпрограммы должно быть уникальным в пределах модуля и других подключаемых модулей и не должно совпадать с именами встроенных.

В подпрограмме могут быть объявлены локальные переменные, видимые только в теле подпрограммы. Раздел объявления переменных находится между заголовком и телом. Пример:

procedure MyProc(Param1: Integer);
var
  i, j: Integer;
begin
  i := 1;end;

Имена локальных переменных могут совпадать с именами глобальных переменных. В этом случае использоваться будет локальная переменная.

Параметры процедур и функций

Синтаксис описания параметров разберем на примере:

function MyFunc(x, y: Integer; a: Double; const S, Str: String; var R: String): Boolean;

Параметры в заголовке подпрограммы называются формальными параметрами. Параметры, передаваемые при вызове подпрограммы, называются фактическими.

Параметры одного типа отделяются запятой, после двоеточия указывается тип параметра. Параметры разных типов отделяются точкой с запятой. Список и тип формальных и фактических параметров должен совпадать. За этим следит компилятор.

Параметры, передаваемые в подпрограмму, делятся на:

  • Параметры-значения. В примере это параметры x, y, a. В качестве фактических параметров могут передаваться константы, произвольные выражения, переменные. Результат вычисления параметра копируется в стек подпрограммы. Если в качестве параметра передается переменная, то изменения производятся над ее копией, а не оригиналом.
  • Параметры-переменные. В примере это параметр R. Перед параметрами указывается служебное слово var. В качестве параметра может передаваться только переменная. Тип фактического параметра должен строго соответствовать типу формального параметра. В процедуру или функцию передается ссылка на переменную. Все изменения производятся непосредственно над переменной.
  • Параметры-константы. В примере это параметры S, Str. Перед параметрами указывается служебное слово const. Параметры-константы похожи на параметры-значения, только в подпрограмму передается ссылка на адрес в памяти, где располагается результат вычисления параметра. Компилятор не позволяет изменять в подпрограмме параметр-константу. Таким образом, не тратится время на копирование параметра в стек подпрограммы, как в случае с параметром-значением.

Пример вызова функции:

var
  a: Word;
  MyStr, MyResult: String;
  b: Boolean;
begin
  b := MyFunc(1, a, AnyFunc(20) + 30 / 0.5, MyStr, ‘Hello, World’, MyResult);
end;

В случае с параметрами-значениями и параметрами-константами тип формального и фактического параметра может быть совместимым. Если формальный определен как целое число, то фактический параметр может быть любого целого типа. Только нужно следить, чтобы формальный параметр мог «вместить» фактический параметр. В данном примере тип формального параметра Y «вмещает» переменную a, т. к. область допустимых значений больше. Целый формальный параметр несовместим с другими типами, в том числе с вещественными. А вот вещественный формальный параметр, совместим с целыми и вещественными.

В случае с параметрами-переменными требуется строгое соответствие типов, даже если формальный и фактический параметры являются родственными, например целыми. Т. е. если формальный параметр имеет тип Integer, то фактический параметр тоже должен иметь тип Integer, а не Byte, Word и т. д. Это касается и классов, даже если фактический параметр является потомком класса формального параметра.

Передача массивов в качестве параметров

Для передачи массива в подпрограмму должен быть определен пользовательский тип:

type
  TMyArr = array [1..20] of Integer;
  TMyArr2d = array [1..10] of array [1..5] of Byte;
  TDynaArr = array of String;
 
procedure MyProc(Arr: TMyArr; var Arr2d: TMyArr2d; DynaArr: TDynaArr);
beginend;
 
procedure AnyProc;
var
  A: TMyArr;
  A2d: TMyArr2d;
  DArr: TDynaArr;
begin
  …
  MyProc(A, A2d, DArr);
end;

Фактический параметр должен быть переменной массива.

Компилятор не сообщит об ошибке, если объявить параметр-массив таким образом:

procedure MyProc(arr: array [1..10] of String);
begin
end;
 
procedure AnyProc;
var
  A: array [1..10] of String;
begin
  MyProc(a);	// type mismatch
end;

Однако во время выполнения вы получите ошибку «type mismatch».

В Паскале есть так называемые открытые массивы. Синтаксис формального параметра открытого массива следующий:

procedure MyProc(OpenArr: array of [тип]);

Открытый массив представляет собой одномерный массив. Нижняя граница массива всегда равна 0, даже если нижняя граница передаваемого массива отличается. Соответственно, верхняя граница равна длине массива минус 1. Для определения границ массива можно воспользоваться функциями Low и High. Пример:

procedure MyProc(Arr: array of Integer);
begin
  // Low(открытый массив) = 0
  for i := Low(Arr) to High(Arr) doend;
 
procedure AnyProc;
var
  arr: array [1..10] of Integer;
  arr2: array [-2..5] of Integer;
begin
  MyProc(arr);
  MyProc(arr2);
  MyProc([1,4,8,9,11,34,23]);
end;

Как видно из примера фактическим параметром может быть не только переменная, но и конструктор массива, который представляет собой перечисление элементов через запятую, заключенных в квадратные скобки.

В движке не поддерживаются вариантные открытые массивы, хотя некоторые встроенные подпрограммы имеют параметры такого типа (например, Format).

procedure Proc(arr: array of const);		// не поддерживается

Возврат значения функции

Функция имеет предопределенную переменную Result, в которую сохраняется возвращаемое значение. Пример:

function SumAB(A, B: Double): Double;
begin
  Result := A + B;
end;

Выход из подпрограммы

В любой момент можно выйти из подпрограммы. Для этого предусмотрен оператор Exit.

В движке не поддерживается передача в EXIT возвращаемого значения.

Exit(-1);				// Компилятор сообщит “internal error(20)”