Подпрограмма – относительно самостоятельная часть программы, имеющая свое имя и выполняющая определенные действия.

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

Структура подпрограммы почти полностью повторяет структуру всей программы и состоит из следующих частей:

  • заголовок подпрограммы
  • раздел описаний
  • тело подпрограммы

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

 

5.1 Процедуры

procedure <имя> (список формальных параметров);

<раздел описаний>;

begin

    <тело процедуры>;

end;

 

Пример 1: Процедура вычисления и вывода на экран куба числа.

procedure cub(x: real);
 var  y: real;
begin
  y:=x*x*x;
  writeln(y);
end;

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

Например:

 cub(5);
  …
readln(z);
cub(z);

 

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

Пример 2: Программа, выводящая на экран следующее:

*********

a=1 b=1

*********

a=2 b=4

*********

a=3 b=9

*********

a=4 b=16

*********

var a,b: integer;
procedure Stars;{без параметров}
var  i: integer;
begin
  for i:=1 to 9 do write('*');
  writeln;
end;
begin
 Stars;
 for a:=1 to 4 do
 begin
   b:=sqr(a);
   writeln('a=',a,' b=',b);
   Stars; {вызов процедуры Stars }
 end;
end.

Пример 3:

var  a,b: integer;
procedure Stroka(ch: char, n: integer);
var  i: integer;
begin
  for i:=1 to n do write(ch);
  writeln;
end;
begin
  Stroka('+',4);
  for a:=1 to 3 do
  begin
    b:=sqr(a);
    writeln('a=',a,' b=',b);
    Stroka('*',8);
  end;
end.

Разберем вызов процедуры Stroka(‘*’,8): ‘*’ и 8 – фактические параметры (т.е. те, которые указаны в скобках после имени процедуры при ее вызове), они присваиваются при вызове формальным параметрам ch и n (т.е. тем, которые указаны в скобках после имени процедуры при ее описании).

Замечание: количество, порядок и тип параметров при вызове процедуры должны совпадать с количеством, порядком и типом параметров, заданным при описании процедуры.

Результат на экране:

++++

a=1 b=1

********

a=2 b=4

********

a=3 b=9

********


5.2 Функции

Функция отличается от процедуры тем, что результат работы функции возвращается в виде значения через ее имя. Вызов процедуры – это отдельный оператор, а вызов функции может быть использован в выражениях вместе с другими операторами (в правой части оператора присваивания или в операторе write/writeln).

Пример:

стандартные процедуры:write, writeln, read, readln, delay, clrscr…

стандартные функции: sqr, sqrt, abs, odd, exp, ln, sin, cos…

Описание функции:

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

<раздел описаний>;

begin

  <тело функции>;

end;

 

Пример 1: f(x,y)=;

function f (x,y: integer): real;
begin
  f:=sqrt(sqr(x)+sqr(y));
end;
begin
  ……
  z:=f(2,3);
  writeln('f=', f(6,8));
end.

Пример 2:

var  s:real;
     i,n: integer;
function fact(k: integer): longint;
var  f: longint;
     j: integer;
begin
  f:=1;
  for j:=1 to k do f:=f*j;
  fact:=f;
end;
begin
  write('Введите n');
  readln(n);
  S:=0;
  for i:=1 to n do s:=(s+1)/(fact(i)+fact(i+1)+fact(i+2));
  writeln('S = ',s:6:4);
end.

5.3 Области действия имен

  1. Имена переменных, описанных в основной программе, называются глобальными и доступны в основной программе и во всех подпрограммах.
  2. Имена переменных, описанных в подпрограмме, называются локальными и доступны в этой подпрограмме и во всех вложенных в нее подпрограммах (это же относится и к именам формальных параметров!)
  3. Имена локальных переменных перекрывают имена глобальных, то есть если имена переменных в основной программе и подпрограмме совпадают, то в подпрограмме недоступна переменная, описанная в основной программе.

 

5.4 Параметры процедур и функций

Формальные параметры – это параметры, используемые при описании процедур и функций

Фактические параметры – это параметры, используемые при вызове процедур и функций.

При вызове подпрограммы формальные параметры заменяются на соответствующие фактические параметры основной программы (!).

Пример:

var  c: integer;
procedure p(b: integer);   {b – формальный параметр}
begin
   …
end;

begin
   …
   p(c);      { с – фактический параметр}
end.

По способу описания и передачи значения формальные параметры делятся на параметры-значения и параметры-переменные.

  • Параметры-значения (имя: тип)

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

 

Пример1:

 

  • Параметры-переменные ( (var имя: тип); )

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

В подпрограмму передается адрес фактического параметра, а не его копия. (Формальный параметр считается синонимом соответствующего фактического параметра). Поэтому фактический параметр, соответствующий параметру-переменной, должен быть переменной того же типа, что и формальный параметр (константы или выражения не допускаются).

 

Пример 1:

 

Пример2: Программа вычисления объема шара V=4/3πR3


5.5 Побочные эффекты при использовании подпрограмм

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

 

Процедура изменяет значение глобальной переменной S (!). Надо добавить в нее описание локальной S:

procedure Sum(n: integer, var f: integer); 
var  i: integer; 
     s: integer; 
begin 
   … 

5.6 Передача массивов в подпрограммы

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

 procedure S(a: array [1..10] of real);

В связи с этим, если в качестве параметра необходимо использовать массив, то делается предварительное описание типа массива:

 type  mas=array [1..10] of real; 
  …
procedure s(a: mas);
begin
  …
end;
var  b: mas;
     c: array [1..10] of real;
begin
   S(b); 
   S(c); { - нельзя!}
    … 

Массив может передаваться как параметр-значение, и как параметр-переменная. Если параметр массив описан со словом var, то изменение массива в подпрограмме приводит к изменению соответствующего массива в основной программе.

Пример:

type  mas=array [1..10] of integer;
var  c,d: mas;
     i: integer;
procedure S(a: mas, var b: mas);
 var  i: integer;
 begin
  for i:=1 to 10 do
  begin
   a[i]:=a[i]*5;
   b[i]:=b[i]*5;
  end;
 end;
begin
 for i:=1 to 10 do
  begin
   c[i]:=1;
   d[i]:=1;
  end;
 s(c,d);
 for i:=1 to 10 do write (c[i]:3); { 1 1 1 1 1 1 …}
 writeln;
 for i:=1 to 10 do write (d[i]:3); { 5 5 5 5 5 5 …}
end.

Следующие три пункта 5.7, 5.8 и 5.9 являются необязательными для изучения, хотя рассмотренный в них материал достаточно широко используется в программировании.


5.7 Параметры-костанты

В Турбо-Паскаль 7.0 существует еще один вид параметров – параметры-константы.

(const имя: тип)

В этом случае в подпрограмму передается адрес фактического параметра, но он защищен от изменений.

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

type  mas=array [1..10] of real;
var   a: mas;
function Summa (const x:mas): integer;
 var s,i: integer;
begin
  s:=0;
  for i:=1 to 10 do s:=s+x[i];
  summa:=s;
end;
begin
  randomize;
  for i:=1 to 10 do a[i]:=random(20);
  writeln(‘сумма элементов массива: ’,summa(a));
end.

5.8 Массивы открытого типа

В Турбо-Паскаль 7.0 в качестве параметров-переменных можно использовать массивы открытого типа, у которых не задаются размеры. В этом случае подпрограмма может обрабатывать массив любого размера. Фактический размер массива может быть определен с помощью функции High. При описании не указывается тип индекса массива. Индексация элементов открытого массива всегда от 0 до значения функции High ( 0..high(x) , где x – имя массива).

function Summa (var x: array of real): real;
var  i: integer;
     s: real;
begin
  s:=0;
  for i:=1 to high(x) do s:=s+x[i];
  summa:=s;
end;

5.9 Рекурсия в подпрограммах

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

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

(0!=1)

1!=1

N!=1*2*3*…*(N-1)*N

алгоритм основан на соотношении N!=(N-1)!*N

Пример:

function fact(N: word): longint;
begin
  if (n=0) or (n=1) then fact:=1
  else fact:=n*fact(n-1);
end;

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

Рекурсивное оформление программы более компактно и эффективно, но не следует забывать об опасности переполнения стека (!).

Схема вызовов при вычислении 5!:

 

Задачи для самоконтроля

5.1. Определите значение переменных a, b после выполнения следующих программ:

 а)
var  a,b: integer;
procedure proc(x: integer; y: integer);
begin
 y:= y+2; x:= x+2;
end;
begin
 a:=15; b:=15;
 proc(a,b);
 writeln('a = ',a,' b= ',b);
end.
 б)
var  a,b: integer;
procedure proc(x: integer; var y: integer);
begin
 y:= y+2; x:= x+2;
end;
begin
 a:=15; b:=15;
 proc(a,b);
 writeln('a = ',a,' b= ',b);
end.
 в)
var  a,b: integer;
procedure proc(var x: integer; y: integer);
begin
 y:= y+2; x:= x+2;
end;
begin
 a:=15; b:=15;
 proc(a,b);
 writeln('a = ',a,' b= ',b);
end.
 г)
var  a,b: integer; 
 procedure proc(x: integer; var y: integer);
begin
 y:= y+2; x:= x+2;
end;
begin
 a:=15; b:=15;
 proc(a,b);
 writeln(‘a = ’,a,’ b= ‘,b);
end.