Введение

Линейные алгоритмы

Алгоритмы с ветвлением

Алгоритмы с повторением

Одномерный массив (вектор)

Двумерный массив (матрица)

Пользовательские процедуры и функции

маркированный список

справочные материалы

маркированный список

теоретические вопросы

маркированный список

задачи,  решения, программы

Строки

Множества

Записи

Файлы

Графика

На главную
              

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

Процедуры и функции в программах предназначены для того, чтобы заменять часто повторяющиеся фрагменты в программе. В самом деле, нудно переписывать раз за разом одни и те же операторы. Гораздо лучше обозвать их каким-либо именем, и когда они очередной раз потребуются, вместо них
поставить это имя. Такой "именованный блок операторов" называется процедурой.
Функции оформляются почти так же, как и процедуры, но используются для вычисления какого-либо одного значения, которое называется результатом функции. Поэтому функция вызывается не как оператор, а как операнд в выражении.
Процедурой, например, является ClrScr (очистка экрана). Она выполняет все действия, необходимые для того, чтобы стереть с экрана всю информацию. Для очистки экрана в программе нам необязательно писать операторы для этих действий. Нам даже неважно, в чем эти действия заключаются. Достаточно написать ClrScr - и экран будет очищен. Отсюда, кстати, следует вторая выгода от применения процедур - "скрытие" ее действий от программы. Написав правильную процедуру, Вы можете, в общем-то, забыть как и что Вы там делали, и применять ее просто по имени (как ClrScr). Кроме того, основная программа становится проще и "прозрачней", из нее легче уяснить, что же она делает.

Различие в вызове процедуры и функции.
Процедура вызывается как оператор:
ClrScr;

а функция как операнд в выражении:
y := sin(x) + 23;
 

I. Описание процедур.

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

program <имя программы>;
<описания различных программных объектов>
<описания процедур и функций>
begin
<тело основной программы>
end.

Синтаксис описания таков:
procedure <имя>;
[ <локальные переменные>; ]
begin
<оператор>; -¬
... ¦ "тело" процедуры
<оператор>; --
end;

<имя> - любой идентификатор.
Тело процедуры - любая последовательность операторов.

В необязательном разделе <локальные переменные> описываются внутрен-ние переменные и т.п. процедуры - то есть те, которые применяются только
в самой процедуре, а вне ее не нужны.

Пример процедуры, которая обнуляет элементы целого массива A размера n:
procedure Zero;
var i: integer;
begin
for i := 1 to n do A[i] := 0;
end;

Переменная i используется как переменная цикла и вне процедуры не нужна. Поэтому она объявлена как локальная.

Что плохо в этой процедуре? Она всегда обнуляет только массив A. Если мне понадобится обнулить массив B - писать другую процедуру? Но действие-то тоже самое. Не лучше ли при вызове сообщить процедуре, какой именно массив она должна обнулить? И тоже касается длины массива. Для A это n. Но длина массива B может храниться в другой переменной!
Иными словами, процедуре часто необходимы параметры - те объекты, с которыми она должна работать и которые меняются от вызова к вызову. Массив, который надо обнулить и его длина могут быть параметрами процедуры обнуления массива.

Но процедуре необходимо имя, с помощью которого она могла бы обращаться к параметру. И в тоже время обнуляемый массив нельзя назвать ни A, ни B, ему вообще нельзя дать какое-то фиксированное имя. Ведь процедуру обнуления можно вызвать с любым массивом! Выход в том, чтобы дать параметрам формальные имена ("псевдонимы") и процедуре при описании указать, какие переменные не настоящие, а псевдонимы и используются для замены настоящих. И в теле процедуры для именования параметров применять формальные имена-псевдонимы.

Формальное имя (говорят формальный параметр) можно подменить любой переменной того же типа. Какой именно - указывается при вызове процедуры.
Так снимается противоречие в именах.
Итак, процедуре необходимо указать список формальных параметров (их имена и типы):

procedure <имя> [ ( <список формальных параметров> ) ];
[ <локальные переменные>; ]
begin
<тело процедуры>
end;

<Список формальных параметров> имеет следующий вид:
[ var] <список имен>: <тип> [; [ var] <список имен>: <тип>...]

Имена в <списке имен> перечисляются через запятую. Перед <списком имен> может стоять var, а может и не стоять. Переменные, перед которыми var стоит, называются формальными параметрами-переменными. Если var нет - такие переменные называются формальными параметрами-значениями (различие между ними см.).
<Список формальных параметров> может отсутствовать (как в Zero). Такая процедура называется процедурой без параметров. В качестве примера рассмотрим процедуру обнуления. Она должно работать с целым массивом "вообще". Назовем это массив  mas. Такого массива на самом деле нет, это формальное имя. При каждом вызове будем подразумевать под ним тот массив, который нам нужен. А чтобы процедура знала, что это ненастоящий массив, занесем это имя в список формальных параметров. Также поступим с длиной массива.

type massiv = array [1..10] of integer;
...
procedure Zero2( var mas: massiv; k: integer);
var i: integer;
begin
for i := 1 to k do mas[i] := 0;
end;

Эту процедуру в отличие от предыдущей можно применять для обнуления любого целого массива.
Отдельное объявление типа массива в разделе type вызвано необходимостью добиться совпадения типов массива при описании и при вызове процедуры. Если в описании задать тип массива явно

procedure Zero2( var mas: array [1..10] of integer; k: integer);
этой процедуре не удастся передать ни один массив из-за несоответствия типов. Этот способ (через type) обязателен для передачи в качестве параметра любого массива, множества, записи и вообще любого нестандартного типа.


II. Описание функций.
 

Синтаксис описания функций очень похож на синтаксис описания процедур:
function <имя> [(<список формальных параметров>)]:<тип возвр.значения>;
[ <локальные переменные>; ]
begin
<тело функции>
end;

Изменено только ключевое слово на function и добавлен <тип возвращаемого значения>. Все остальное имеет тот же смысл.
Как мы говорили, функция предназначена для вычисления одного значения, которое и является ее результатом. Он "возвращается" туда, откуда функция была вызвана. Чтобы определить, какой тип должен иметь результат, и указывается <тип возвращаемого значения>. Для функции sin этот тип, например, real.
Существует также одно требование для <тела функции>. Оно заключается в том, что на любой ветви выполнения функции должен встретиться хотя бы один оператор вида

<имя функции> := <выражение>;

<выражение> должно иметь результат типа <тип возвращаемого значения>. С помощью этого оператора <имя функции> ассоциируется с ее результатом (иными словами Вы сообщаете, какой же результат имеет функция). Если этого оператора не будет, значение функции останется неопределено.
Внимание! <имя функции> - это не переменная и употребление его справа от знака присваивания и т.п. запрещено! Например, нельзя записать
<имя функции> := <имя функции> + 1;
или сравнить <имя функции> с чем-то.
Для примера приведу функцию, вычисляющую сумму элементов целого массива:

type massiv = array [1..10] of integer;
...
function summa ( var mas: massiv; k: integer) : integer;
var s, i: integer;
begin
s := 0;
for i := 1 to k do s := s + mas[i];

summa := s;
end;

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

III. Параметры-значения и параметры-переменные.
 

По способу использования в процедуре (функции) параметры делятся на на две группы: параметры-значения и параметры-переменные.

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

Параметрами-значениями должны быть те параметры, значение которых в процедуре не изменяется, не должно изменяться, либо изменения могут быть потеряны без вреда для результата работы. Это, так сказать, "пассивные" параметры, они служат только для передачи процедуре каких-то данных.
При получении процедурой параметра-переменной картина совершенно иная! Создается ссылка на переданную переменную, или, иначе говоря, ее синоним. Все изменения, которые происходят с синонимом, изменяют и переданную переменную. По окончании процедуры ссылка-синоним уничтожается, но все изменения сохраняются!

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

Кроме того, параметрами-переменными рекомендуется делать крупные программные объекты типа больших массивов, файлов, множеств и т.п. Объясняется это тем что для создания копии массива из 10000 целых чисел необходимо 20000 байт памяти (которой может и не быть), а также относительно большое время. На создание же ссылки тратятся в 10000 меньшие ресурсы. Зачем попусту расходовать память и время?
В процедуре Zero2 обнуляемый массив передается как параметр-переменная - потому что мы должны обнулить реальный массив, переданный этой процедуре (в этом и весь смысл), а не его копию.

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

IV. Глобальные и локальные переменные. Правило скрытия имен.
 

Переменные и вообще любые программные объекты, объявленные в основной программе, называются глобальными ("всеобщими").
Программные объекты, объявленные в процедуре или функции, называются локальными ("местными","внутренними").
Глобальные объекты доступны (т.е. ими можно пользоваться) в любом месте программы, за исключением тех случаев, когда действует правило скрытия имен (см. ниже).
Локальные объекты доступны только в пределах той процедуры или функции, в которой они описаны. Вне этой процедуры или функции они не существуют!
В том случае, если в процедуре или функции объявлена локальная переменная с именем уже существующей глобальной, глобальная переменная становится недоступной и на все области действия данной локальной переменной это имя обозначает именно ее. То же касается имен любых других программных объектов. Это называется правило скрытия имен.

Например:

program prim71;
var i: integer;
...
procedure Down;
var i: integer;
begin
i := 0; { глобальная переменная скрыта, значение }
end; { присваивается локальной переменной }
...
begin
i := 10; { значение присваивается глобальной переменной }
Down;
writeln(i); { будет выведено 10, а не 0 }
end.

Более того, локальная и глобальная переменные с одним именем могут иметь даже разный тип! В примере локальная i могла иметь тип real.

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

 

V. Вызов процедур.
 

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

<имя процедуры> [ (<список фактических параметров>) ];

Этот оператор называется оператором вызова процедуры.
Конечно же, фактических параметров должно быть столько же, сколько и формальных. Они должны располагаться в правильном порядке (первому формальному параметру соответствует первый фактический, второму формальному - второй фактический, третьему - третий и т.д.). Тип каждого фактического параметра должен совпадать с типом соответствующего формального параметра (если на данном месте стоит формальный параметр-целый массив, в списке фактических параметров на этом месте должен стоять целый массив того же типа).
Фактические параметры перечисляются через запятую.
Для формальных параметров-значений разрешается использовать в качестве фактического любое выражение, имеющее результат того же типа (в частности это может быть константа, переменная и т.д.). В качестве передаваемого значения будет использован результат этого выражения.
Для формальных параметров-переменных соответствующий фактический может быть только переменной того же типа. Это вызвано тем, что на выражение ссылку завести нельзя и значение ему не присвоишь. А это вполне может случиться с формальным параметром-переменной.
Примеры (с процедурой Zero2).

var A, B: massiv;
i, k: integer;
...
i := 2;
k := -3;
...
Zero2(A, 10); { обнулить 10 элементов A }
Zero2(B, i + k + 5); { обнулить 4 элемента B }
Zero2(B, i); { обнулить 2 элемента B }
Zero2(A, abs(k)); { обнулить 3 элемента A }

 

VI. Вызов функций.
 

При вызове функций используется та же конструкция, что и при вызове процедур:

<имя функции> [ (<список фактических параметров>) ]

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

sin(x);

так как функция sin вернет свой результат (т.е. синус x) туда, откуда она была вызвана, и получится бессмысленный оператор вроде
0.45872е00;
(если принять, что результат будет 0.45872е00). Правильной будет запись
y := sin(x);
после того как функция вернет свой результат, получится
y := 0.45872е00;
а это обыкновенное присваивание значения переменной y.
На самом деле все происходит совершенно иначе, но смысл именно такой:
при выполнении программы функция заменяется своим значением, и в том месте программы, где Вы употребили функцию, должно быть разрешено использовать значение этого типа.
Итак, вызов функции можно использовать только в тех местах программы, где разрешено использование выражений того же типа, что и результат функции.
Из этого следует, что функцию нельзя вызывать как отдельный оператор, вызывать слева от знака присваивания или использовать в качестве переменной цикла for
.Зато ее можно вызывать справа от знака присваивания, как фактический параметр-значение процедуры или функции, как начальное или конечное значение того же цикла for и т.п. Не надо только забывать, что во всех случаях производится контроль типов и функция должна возвращать результат разрешенного в данном месте типа.

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

var A, B: massiv;
s1, s2, i, k: integer;
...
k := 10;
i := 3;
s1 := summa(A,10);
s2 := s1 - summa(B, k-i) div 2;
writeln('Сумма 3х элементов A равна ', summa(A, i));
for i := 1 to summa(B,5) do ...

ПОЛЕЗНЫЕ СОВЕТЫ.
 

1. Обращайте внимание на имена локальных переменных в процедуре или функции. Если они случайно совпадут с именами глобальных объектов, то эти глобальные объекты окажутся недоступными в данной процедуре (функции).
2. Не включайте в тело процедуры (функции) операторы ввода-вывода без действительной необходимости.
3. Проверьте, через какие параметры в процедуре Вы передаете результат и не забудьте сделать их параметрами-переменными.
4. Нельзя вызывать функцию как отдельный оператор.
5. Не рекомендуется передавать процедуре (функции) данные через глобальные переменные.
6. Не рекомендуется применять при описании функций параметры-переменные.

Сайт создан в системе uCoz