Глиф Учебники

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

Курс «Pascal» · урок 8 из 9 · 4 минуты чтения

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

Процедура

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
----------

Как это устроено:

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

Функция

Функция считает значение и возвращает его. В заголовке после скобок через двоеточие пишут тип результата, а внутри присваивают результат имени функции:

Pascal
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(Max2(7, 12)); на экране 12 7 → a, 12 → b результат 12 функция function Max2(a, b: integer): integer; if a > b then Max2 := a else Max2 := b; a 7 b 12 7 > 12 — нет, ветка else
При вызове Max2(7, 12) число 7 попадает в параметр a, 12 — в b; условие a > b ложно, поэтому выполняется Max2 := b, и результат 12 возвращается в writeln.

Функцию вызывают внутри выражения — там, где нужно значение: в 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:

Pascal
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 — копии программа x 3 y 8 копии a 8 b 3 Swap меняет только свои копии x и y остались 3 и 8 С var — сами переменные программа x 3 8 y 8 3 a b Swap(var a, b: integer) a и b — другие имена x и y
Обычный параметр получает копию: без var процедура Swap поменяла бы местами свои копии, а x и y остались бы 3 и 8; с var параметры a и b — другие имена x и y, и после вызова x = 8, y = 3.

Через var-параметры процедура может вернуть сразу несколько значений:

Pascal
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 можно ставить и после подпрограмм — тогда эти переменные подпрограммам не видны и случайно их не испортят.

Локальные и глобальные переменные

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

Хорошее правило — всё, что нужно подпрограмме, передавать через параметры, а для своих счётчиков заводить локальные переменные. Параметр цикла for внутри подпрограммы обязательно описывайте в её собственном var: PascalABC.NET не разрешает использовать для этого глобальную переменную.

Локальные переменные в Free Pascal не обнуляются при вызове: в них лежит то, что осталось в памяти. Счётчики и суммы внутри подпрограмм всегда обнуляйте сами, иначе результат будет случайным.

exit — досрочный выход

exit сразу завершает подпрограмму. В функции перед этим нужно присвоить результат:

Pascal
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.

Pascal
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). Без него подпрограмма будет вызывать себя бесконечно, пока программа не упадёт с переполнением стека.

Fact(5) = 5 * Fact(4) 120 Fact(4) = 4 * Fact(3) 24 Fact(3) = 3 * Fact(2) 6 Fact(2) = 2 * Fact(1) 2 Fact(1) = 1 1 результаты — вверх вызовы — вниз стоп: n <= 1
Fact(5) вызывает Fact(4), та — Fact(3) и так до Fact(1), которая сразу возвращает 1, после чего результаты собираются обратно: 2, 6, 24 и 120.

Частые ошибки

Коротко

Проверьте себя

1. Чем функция отличается от процедуры?
2. Что выведет программа?
Pascal
var
  x: integer;

procedure AddOne(n: integer);
begin
  n := n + 1;
end;

begin
  x := 5;
  AddOne(x);
  writeln(x);
end.
3. Как в функции Square вернуть результат так, чтобы это работало и в PascalABC.NET, и в Free Pascal в обычном режиме?
4. Где описывают процедуры и функции?