Свойства функций

1. Что такое функция?

Функция – это подпрограмма, которая возвращает значение.

2. Как объявляется функция?

При объявлении функции необходимо задать имя функции, список формальных параметров, которые она будет получать, а также тип возвращаемого значения. Эта часть объявления называется заголовком функции. После заголовка следует блок, который выполняется при вызове функции. Эта часть объявления называется телом функции.

3. Как выглядит заголовок функции?

function <имя функции> (<список формальных параметров>): <тип возвращаемого значения>;

4. Каков может быть тип возвращаемого значения функции?

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

5. Как осуществляется возврат значения из функции?

Само имя функции ведёт себя как специальная переменная, в которой хранится возвращаемое функцией значение. Так же ведёт себя предопределённая переменная result, которая неявно объявляется в каждой функции, если включено использование расширенного синтаксиса.

В теле функции имени функции или переменной result можно присваивать значение несколько раз. Последнее присвоенное значение, независимо от того, было ли оно присвоено имени функции или переменной result, возвращается как результат функции. Присваиваемое значение должно соответствовать объявленному типу возвращаемого значения. Имя функции и переменная result всегда хранят одно и то же значение.

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

Если функция завершается без присваивания какого-либо значения имени функции или переменной result, то результат функции считается неопределённым.

6. Как выглядит вызов функции?

Для вызова функции, также как и для вызова процедуры, необходимо указать имя функции и фактические параметры. Однако вызов функции является выражением и должен быть использован как выражение. Выражения в программе встречаются справа от знака присваивания, в условиях, в операторах вывода, в вызовах других процедур и функций. 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)); // Использование результата функции в вызове процедуры

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

7. Что происходит при вызове функции?

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

8. Как передаются данные в функцию и из функции?

Входные данные функции передаются через параметры. Количество параметров не ограничено, соответственно, функция может иметь любое количество входных параметров. Входные параметры обычно передаются как параметры-значения или параметры-константы. Результат функции передаётся специальным образом через имя функции или переменную result. Однако это не означает, что функция не может передать данные обратно в вызывающую программу через параметры-переменные или выходные параметры.

9. Что такое формальные параметры?

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

Список формальных параметров определяется количество, порядок и типы параметров, которые должны быть переданы в процедуру при вызове. Список формальных параметров представляет собой последовательность объявлений, разделённых точкой с запятой, заключённую в круглые скобки. Каждое объявление – это разделяемый запятыми список имён параметров, после которого следует двоеточие и тип. Кроме того, каждому объявлению может предшествовать одно из ключевых слов var, const или out.

10. Что такое фактические параметры?

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

Список фактических параметров – это список вполне конкретных значений, которые реально передаются в процедуру и которые она обрабатывает. Формальные параметры – это, в общем-то, абстракция. Фактические параметры должны реально существовать, т.е. это должна быть объявленная и обычно проинициализированная переменная, константа или выражение.

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

11. Какая связь между формальными и фактическими параметрами?

Список фактических параметров должен соответствовать списку формальных параметров по следующим критериям.

  1. По количеству. Т.е. если в заголовке процедуры объявлены три формальных параметра, то и фактических параметров должно быть тоже три, не больше и не меньше. Это самый простой критерий, однако, о нём почему-то часто забывают как при разработке программ, так и на экзамене.
  2. По типу. Тип фактического параметра должен либо строго соответствовать типу формального параметра, либо быть совместимым типом. Это зависит от категории параметра – параметр-константа, параметр-значение, параметр-переменная, выходной параметр. Надо помнить о том, что тип в данном случае включает также и структуру переменной, и что компилятор языка Паскаль проверяет соответствие типов по имени.
  3. По порядку следования. Тут возможны две ситуации – отслеживаемая компилятором и не отслеживаемая. Например, если надо передать в процедуру символ и целое число, то попытка передать целое число и символ приведёт к возникновению ошибки. Если же надо передать два целых числа, то компилятор не может отследить правильность их порядка. Но если перепутать местами количество строк и столбцов матрицы, то результат будет неадекватный. Т.е. при передаче параметров надо не только формально учитывать тип, но и смысл каждого параметра.

Имена формальных и фактических параметров совпадать не должны.

12. Что такое параметры-константы?

Параметр-константа – это формальный параметр, объявленный с ключевым словом const. Тип соответствующего фактического параметра должен быть совместимым, в качестве фактического параметра может быть использовано выражение.

Параметр-константа ведёт себя как локальная константа. Параметры-константы сходны с параметрами-значениями, но в теле процедуры нельзя изменить параметр-константу и нельзя передать её в другую процедуру в качестве параметра-переменной.

Параметры-константы структурных и строковых типов используются для оптимизации компилятором кода для передачи этих параметров, т.е. они могут передаваться и по ссылке – это решает компилятор. Параметры-константы базовых типов на эффективность передачи не влияют, они лишь говорят о характере использования такого параметра.

13. Что такое параметры-значения?

Параметр-значение – это формальный параметр, объявленный без использования ключевых слов. Тип соответствующего фактического параметра должен быть совместимым, в качестве фактического параметра может быть использовано выражение.

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

14. Что такое параметры-переменные?

Параметр-переменная – это формальный параметр, объявленный с ключевым словом var. Тип соответствующего фактического параметра должен быть идентичным, в качестве фактического параметра может быть использована только переменная.

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

15. Что такое выходные параметры?

Выходной параметр – это формальный параметр, объявленный с ключевым словом out. Тип соответствующего фактического параметра должен быть идентичным, в качестве фактического параметра может быть использована только переменная.

Выходные параметры, как и параметры-переменные, передаются в процедуру по ссылке. Отличие состоит в том, что выходные параметры рассматриваются именно как выходные, и значение переменной, являющейся фактическим параметром, игнорируется.

16. Что такое область видимости?

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

<объявления> begin <операторы> end;

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

Параметры процедур и функций относятся к области видимости своей процедуры или функции и не доступны из других блоков, в том числе и из основной программы.

Два «параллельных» блока образуют непересекающиеся области видимости, поэтому при объявлении в них одинаковых идентификаторов никаких конфликтов не возникает. Если идентификатор, объявленный в охватывающем блоке, переобъявляется во внутреннем блоке, то локальное, внутреннее объявление имеет преимущество и определяет значение этого идентификатора во внутреннем блоке.

17. Что такое локальные и глобальные объявления?

Блок программы является самым внешним блоком. Идентификаторы, объявленные в нём, называются глобальными. Идентификаторы, объявленные в других блоках, называются локальными.

18. Как инициализируются локальные и глобальные переменные?

Локальные переменные не могут иметь инициализаторов. Глобальные переменные могут быть проинициализированы при объявлении. Если инициализатор отсутствует, компилятор инициализирует глобальную переменную значением 0. Локальные переменные автоматически не инициализируются!

19. Когда можно вызвать одну подпрограмму из другой?

Две процедуры, объявленные в одном охватывающем блоке, находятся в одной области видимости и могут быть вызваны одна из другой, однако вызываемая процедура должна быть объявлена раньше вызывающей, т.к. любой идентификатор доступен только ниже точки своего объявления.

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

20. Примеры использования функций

  1. Проверить, что заданная строка матрицы содержит наибольшее количество отрицательных элементов.
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');
  1. Определить номера строк матрицы, сумма элементов которых положительна.
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 всегда будет сильнее любого глобального объявления этого имени, поэтому внутренняя функции будет ссылаться на правильный объект.

Однако такая минимизация количества параметров допустима лишь для подобных случаев локального использования процедур и функций. Во всех остальных случаях процедуры и функции не должны использовать глобальные переменные.

  1. В одном массиве определить, сколько раз встречается максимальный элемент. В другом массиве определить количество элементов, больших max / 2.
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));