Урок 8. Процедуры и функции
Когда программа растёт, одни и те же куски кода повторяются, а читать её становится трудно. Подпрограмма даёт куску кода имя: его пишут один раз, а вызывают сколько угодно раз. В Pascal подпрограммы бывают двух видов: процедура просто выполняет действия, функция ещё и возвращает значение.
Процедура
program Lines;
procedure Line(n: integer; c: char);
var
i: integer;
begin
for i := 1 to n do
write(c);
writeln;
end;
begin
Line(10, '*');
writeln('Pascal');
Line(10, '-');
end.**********
Pascal
----------Как это устроено:
- подпрограммы описывают в разделе описаний — обычно после
varпрограммы — и обязательно до её главногоbegin; - в заголовке — имя и параметры в скобках:
n: integer; c: char. Параметры разных типов разделяют точкой с запятой, одного типа можно перечислить через запятую:a, b: integer; - у подпрограммы может быть свой раздел
var— локальные переменные, они существуют только во время её работы; - тело —
begin … end;с точкой с запятой, а не с точкой: точка ставится только в конце всей программы; - вызов — имя и значения параметров в том же порядке:
Line(10, '*').
Подпрограмму нужно описать выше места, где её вызывают, иначе компилятор сообщит, что не знает такого имени.
Функция
Функция считает значение и возвращает его. В заголовке после скобок через двоеточие пишут тип результата, а внутри присваивают результат имени функции:
program Functions;
function Max2(a, b: integer): integer;
begin
if a > b then
Max2 := a
else
Max2 := b;
end;
function Cube(x: integer): integer;
begin
Cube := x * x * x;
end;
begin
writeln(Max2(7, 12));
writeln(Max2(Max2(3, 9), 5));
writeln(Cube(4));
end.12
9
64Функцию вызывают внутри выражения — там, где нужно значение: в writeln, в присваивании m := Max2(x, y), в условии if Cube(n) > 100 then. Можно передать результат одной функции в другую, как в Max2(Max2(3, 9), 5).
Вместо имени функции результат можно присваивать переменной Result: Result := a. Так можно в PascalABC.NET и в Free Pascal в режимах objfpc и delphi. В обычном режиме Free Pascal Result нет — поэтому в курсе результат присваивается имени функции, это работает везде.
Параметры по значению и var-параметры
Обычный параметр — копия: подпрограмма получает значение, и если она его поменяет, переменная в вызывающей программе останется прежней. Чтобы подпрограмма могла изменить переменную, перед параметром пишут var:
program SwapDemo;
var
x, y: integer;
procedure Swap(var a, b: integer);
var
t: integer;
begin
t := a;
a := b;
b := t;
end;
begin
x := 3;
y := 8;
Swap(x, y);
writeln('x = ', x, ', y = ', y);
end.x = 8, y = 3Без слова var процедура поменяла бы местами свои копии, а x и y остались бы 3 и 8. В var-параметр можно передать только переменную: Swap(3, 8) — ошибка, числам нечего «возвращать».
Через var-параметры процедура может вернуть сразу несколько значений:
program MinMaxProc;
procedure MinMax(a, b, c: integer; var mn, mx: integer);
begin
mn := a;
if b < mn then
mn := b;
if c < mn then
mn := c;
mx := a;
if b > mx then
mx := b;
if c > mx then
mx := c;
end;
var
lo, hi: integer;
begin
MinMax(7, -2, 5, lo, hi);
writeln('Наименьшее: ', lo, ', наибольшее: ', hi);
end.Наименьшее: -2, наибольшее: 7Раздел var можно ставить и после подпрограмм — тогда эти переменные подпрограммам не видны и случайно их не испортят.
Локальные и глобальные переменные
- Глобальные переменные описаны в разделе
varпрограммы выше подпрограмм. Их видят и главная программа, и все подпрограммы ниже описания. - Локальные описаны внутри подпрограммы. Их видит только она сама, и при каждом вызове они создаются заново.
Если локальная переменная называется так же, как глобальная, внутри подпрограммы действует локальная.
Хорошее правило — всё, что нужно подпрограмме, передавать через параметры, а для своих счётчиков заводить локальные переменные. Параметр цикла for внутри подпрограммы обязательно описывайте в её собственном var: PascalABC.NET не разрешает использовать для этого глобальную переменную.
Локальные переменные в Free Pascal не обнуляются при вызове: в них лежит то, что осталось в памяти. Счётчики и суммы внутри подпрограмм всегда обнуляйте сами, иначе результат будет случайным.
exit — досрочный выход
exit сразу завершает подпрограмму. В функции перед этим нужно присвоить результат:
program ZeroDigit;
function HasZero(n: longint): boolean;
begin
HasZero := false;
while n > 0 do
begin
if n mod 10 = 0 then
begin
HasZero := true;
exit;
end;
n := n div 10;
end;
end;
begin
if HasZero(1203) then
writeln('В числе 1203 есть ноль');
if not HasZero(456) then
writeln('В числе 456 нуля нет');
end.В числе 1203 есть ноль
В числе 456 нуля нетПараметр n здесь обычный, поэтому функция спокойно делит свою копию, а число в вызове не меняется.
Рекурсия
Подпрограмма может вызывать сама себя — это рекурсия. Классический пример — факториал: n! = n · (n − 1)!, а 1! = 1.
program RecursiveFact;
function Fact(n: integer): longint;
begin
if n <= 1 then
Fact := 1
else
Fact := n * Fact(n - 1);
end;
begin
writeln('5! = ', Fact(5));
writeln('10! = ', Fact(10));
end.5! = 120
10! = 3628800У рекурсии обязательно должен быть случай без вызова самой себя (здесь n <= 1). Без него подпрограмма будет вызывать себя бесконечно, пока программа не упадёт с переполнением стека.
Частые ошибки
end.в конце подпрограммы. Тело подпрограммы закрываютend;, точка — только в конце всей программы.- Подпрограмма ниже вызова — компилятор не знает такого имени. Перенесите её выше главного
begin. - Результат функции не присвоен (например, только в одной ветке
if) — функция вернёт не то, что нужно. Free Pascal предупреждает об этом:Function result does not seem to be set. - Забыт
varу параметра, который подпрограмма должна изменить, — она меняет свою копию, а переменная в вызове остаётся прежней. - Локальная сумма не обнулена — в Free Pascal в ней лежит «мусор».
Коротко
- Процедура выполняет действия, функция возвращает значение; обе описывают до главного
begin. - Результат функции присваивают её имени:
Max2 := a. - Обычный параметр — копия,
var-параметр — сама переменная, через него подпрограмма меняет данные вызывающего. - Свои переменные подпрограммы описывают в её разделе
var, и их нужно задавать до использования. exitзавершает подпрограмму досрочно; у рекурсии должен быть случай остановки.