Функция – это подпрограмма, которая возвращает значение.
При объявлении функции необходимо задать имя функции, список формальных параметров, которые она будет получать, а также тип возвращаемого значения. Эта часть объявления называется заголовком функции. После заголовка следует блок, который выполняется при вызове функции. Эта часть объявления называется телом функции.
function <имя функции> (<список формальных параметров>): <тип возвращаемого значения>;
Тип возвращаемого значения может быть любым стандартным или пользовательским типом, кроме файлов.
Само имя функции ведёт себя как специальная переменная, в которой хранится возвращаемое функцией значение. Так же ведёт себя предопределённая переменная result, которая неявно объявляется в каждой функции, если включено использование расширенного синтаксиса.
В теле функции имени функции или переменной result можно присваивать значение несколько раз. Последнее присвоенное значение, независимо от того, было ли оно присвоено имени функции или переменной result, возвращается как результат функции. Присваиваемое значение должно соответствовать объявленному типу возвращаемого значения. Имя функции и переменная result всегда хранят одно и то же значение.
Но имя функции и переменная result не взаимозаменяемы. Когда имя функции стоит слева от знака присваивания, компилятор считает, что имя функции используется для сохранения результата функции. Если же имя функции встречается в других частях тела функции, компилятор рассматривает эту запись как рекурсивный вызов функции. Переменная result всегда рассматривается как переменная, и может быть использована в операциях и вызовах других подпрограмм.
Если функция завершается без присваивания какого-либо значения имени функции или переменной result, то результат функции считается неопределённым.
Для вызова функции, также как и для вызова процедуры, необходимо указать имя функции и фактические параметры. Однако вызов функции является выражением и должен быть использован как выражение. Выражения в программе встречаются справа от знака присваивания, в условиях, в операторах вывода, в вызовах других процедур и функций.
function Average(const x: mas; n: integer): real; // Функция подсчёта среднего арифметического
procedure Change(var x: mas; n: integer; v: real); // Процедура замены элементов, больших заданного числа
s := Average(a, na); // Присваивание результата функции в переменную
s := Average(a, na) + Average(b, nb); // Сложение результатов двух вызовов функции
writeln('Average of array A is ', Average(a, na)); // Вывод результата функции
if Average(a, na) > 0 then ... // Использование результата функции в условии
Change(a, na, Average(a, na)); // Использование результата функции в вызове процедуры
При использовании расширенного синтаксиса возможно использование вызова функции как оператора. В этом случае возвращаемое функцией значение игнорируется. Однако в подавляющем большинстве случаев нет смысла в таком использовании функций.
При вызове функции управление передаётся из точки вызова в функцию, при этом ей передаются указанные фактические параметры. Затем выполняется тело функции. По завершении выполнения функции организуется передача значения из функции, и управление передаётся обратно в точку вызова. Это означает, что вычисляется выражение, составной частью которого является вызов функции, а затем выполняется оператор, в котором находится это выражение.
Входные данные функции передаются через параметры. Количество параметров не ограничено, соответственно, функция может иметь любое количество входных параметров. Входные параметры обычно передаются как параметры-значения или параметры-константы. Результат функции передаётся специальным образом через имя функции или переменную result. Однако это не означает, что функция не может передать данные обратно в вызывающую программу через параметры-переменные или выходные параметры.
Параметры, записанные в заголовке процедуры, называются формальными. Список формальных параметров – это список неких условных переменных. Он описывает данные, которые должны быть переданы в процедуру, в общем виде.
Список формальных параметров определяется количество, порядок и типы параметров, которые должны быть переданы в процедуру при вызове. Список формальных параметров представляет собой последовательность объявлений, разделённых точкой с запятой, заключённую в круглые скобки. Каждое объявление – это разделяемый запятыми список имён параметров, после которого следует двоеточие и тип. Кроме того, каждому объявлению может предшествовать одно из ключевых слов var, const или out.
Параметры, записанные в вызове процедуры, называются фактическими.
Список фактических параметров – это список вполне конкретных значений, которые реально передаются в процедуру и которые она обрабатывает. Формальные параметры – это, в общем-то, абстракция. Фактические параметры должны реально существовать, т.е. это должна быть объявленная и обычно проинициализированная переменная, константа или выражение.
Список фактических параметров представляет собой список выражений, разделённых запятыми. Значения этих выражений подставляются вместо формальных параметров последовательно, т.е. значение первого фактического параметра – вместо первого формального параметра, значение второго фактического параметра – вместо второго формального параметра и т.д.
Список фактических параметров должен соответствовать списку формальных параметров по следующим критериям.
Имена формальных и фактических параметров совпадать не должны.
Параметр-константа – это формальный параметр, объявленный с ключевым словом const. Тип соответствующего фактического параметра должен быть совместимым, в качестве фактического параметра может быть использовано выражение.
Параметр-константа ведёт себя как локальная константа. Параметры-константы сходны с параметрами-значениями, но в теле процедуры нельзя изменить параметр-константу и нельзя передать её в другую процедуру в качестве параметра-переменной.
Параметры-константы структурных и строковых типов используются для оптимизации компилятором кода для передачи этих параметров, т.е. они могут передаваться и по ссылке – это решает компилятор. Параметры-константы базовых типов на эффективность передачи не влияют, они лишь говорят о характере использования такого параметра.
Параметр-значение – это формальный параметр, объявленный без использования ключевых слов. Тип соответствующего фактического параметра должен быть совместимым, в качестве фактического параметра может быть использовано выражение.
Параметр-значение ведёт себя как локальная переменная, которая инициализируется значением, передаваемым при вызове процедуры. Если в качестве параметра-значения передаётся переменная, то компилятор создаёт её копию, и процедура работает именно с копией. Изменения копии никак не влияют на исходную переменную. Если в качестве параметра-значения передаётся выражение, которому вообще не может быть присвоено новое значение, компилятор записывает его значение в специально выделенную область памяти, и процедура работает с этой областью памяти. После завершения работы процедуры копия переменной или выделенная область памяти освобождаются.
Параметр-переменная – это формальный параметр, объявленный с ключевым словом var. Тип соответствующего фактического параметра должен быть идентичным, в качестве фактического параметра может быть использована только переменная.
Параметр-переменная ведёт себя как указатель на переменную, являющуюся фактическим параметром. Все действия, производимые процедурой, производятся именной над переменной, являющейся фактическим параметром. Соответственно процедура может изменить значение этой переменной.
Выходной параметр – это формальный параметр, объявленный с ключевым словом out. Тип соответствующего фактического параметра должен быть идентичным, в качестве фактического параметра может быть использована только переменная.
Выходные параметры, как и параметры-переменные, передаются в процедуру по ссылке. Отличие состоит в том, что выходные параметры рассматриваются именно как выходные, и значение переменной, являющейся фактическим параметром, игнорируется.
Программа на языке Паскаль, а также тела процедур и функций представляют собой так называемые блоки.
<объявления>
begin
<операторы>
end;
Каждый такой блок образует так называемую область видимости, т.е. все идентификаторы, объявленные в каком-либо блоке, доступны в этом блоке, а также во вложенных блоках, но не доступны в охватывающих и «параллельных» блоках.
Параметры процедур и функций относятся к области видимости своей процедуры или функции и не доступны из других блоков, в том числе и из основной программы.
Два «параллельных» блока образуют непересекающиеся области видимости, поэтому при объявлении в них одинаковых идентификаторов никаких конфликтов не возникает. Если идентификатор, объявленный в охватывающем блоке, переобъявляется во внутреннем блоке, то локальное, внутреннее объявление имеет преимущество и определяет значение этого идентификатора во внутреннем блоке.
Блок программы является самым внешним блоком. Идентификаторы, объявленные в нём, называются глобальными. Идентификаторы, объявленные в других блоках, называются локальными.
Локальные переменные не могут иметь инициализаторов. Глобальные переменные могут быть проинициализированы при объявлении. Если инициализатор отсутствует, компилятор инициализирует глобальную переменную значением 0. Локальные переменные автоматически не инициализируются!
Две процедуры, объявленные в одном охватывающем блоке, находятся в одной области видимости и могут быть вызваны одна из другой, однако вызываемая процедура должна быть объявлена раньше вызывающей, т.к. любой идентификатор доступен только ниже точки своего объявления.
Процедуры и функции могут содержать объявления других процедур и функции в локальном блоке объявления. Вложенные процедуры и функции доступны только в блоке, где они объявлены и не могут быть вызваны из других блоков, в том числе и из основной программы.
function IsMaxNegatives(const x: matrix; m, n, k: integer): boolean;
var
neg: integer;
i: integer;
function Negatives(i: integer): integer;
var
j: integer;
begin
result := 0;
for j := 1 to n do
if x[i, j] < 0 then
result := result + 1;
end;
begin
neg := Negatives(k);
result := true;
for i := 1 to k - 1 do
if neg < Negatives(i) then
begin
result := false;
exit;
end;
for i := k + 1 to m do
if neg < Negatives(i) then
begin
result := false;
exit;
end;
end;
...
for i := 1 to m do
if IsMaxNegatives(a, m, n, i) then
writeln(f, 'The ', i, ' row has the maximum of negative values')
else
writeln(f, 'The ', i, ' row doesn''t have the maximum of negative values');
procedure RowsWithPositiveSum(const x: matrix; m, n: integer; var r: mas; var k: integer);
var
i, j: integer;
function Sum(i: integer): real;
var
j: integer;
begin
result := 0;
for j := 1 to n do
result := result + x[i, j];
end;
begin
k := 0;
for i := 1 to m do
if Sum(i) > 0 then
begin
k := k + 1;
r[k] := i;
end;
end;
...
RowsWithPositiveSum(a, ma, na, ra, ka);
RowsWithPositiveSum(b, mb, nb, rb, kb);
В данных примерах внутренние функции имеют минимальное количество параметров и ссылаются на обрабатываемый массив как на «глобальную» переменную (хотя, конечно, в данном случае это не глобальная переменная). Это сделано для уменьшения передач параметров – поскольку функции локальные, они не могут быть вызваны из других процедур и функций и из основной программы, и используются только в той процедуре или функции, где они определены. При этом локальное объявление имени x всегда будет сильнее любого глобального объявления этого имени, поэтому внутренняя функции будет ссылаться на правильный объект.
Однако такая минимизация количества параметров допустима лишь для подобных случаев локального использования процедур и функций. Во всех остальных случаях процедуры и функции не должны использовать глобальные переменные.
function Max(const x: mas; n: integer): integer;
function MaxCount(const x: mas; n: integer): integer;
var
m, i: integer;
begin
m := Max(x, n);
result := 0;
for i := 1 to n do
if x[i] = m then
result := result + 1;
end;
function CountOfGreaters(const x: mas; n, v: integer): integer;
...
writeln(f, 'Count of maximums in the array A is ', MaxCount(a, na));
writeln(f, 'Count of elements greater max / 2 in the array B is ', CountOfGreaters(b, nb, Max(b, nb) div 2));