Подпрограмма – относительно самостоятельная часть программы, имеющая свое имя и выполняющая определенные действия.
Подпрограммы повышают надежность и наглядность программ, т.к. позволяют разрабатывать и отлаживать каждый блок программы независимо (например, разными людьми).
Структура подпрограммы почти полностью повторяет структуру всей программы и состоит из следующих частей:
В Паскаль имеются два вида подпрограмм – процедуры и функции. Они отличаются назначеием и способом их использования. Процедуры служат для выполнения определенной последовательности действий, направленных на изменение программной обстановки (изменение значений переменных, ввод/вывод данных и т.п.). Функции для вычисления значения выражения.
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
********
Функция отличается от процедуры тем, что результат работы функции возвращается в виде значения через ее имя. Вызов процедуры – это отдельный оператор, а вызов функции может быть использован в выражениях вместе с другими операторами (в правой части оператора присваивания или в операторе 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.
Формальные параметры – это параметры, используемые при описании процедур и функций
Фактические параметры – это параметры, используемые при вызове процедур и функций.
При вызове подпрограммы формальные параметры заменяются на соответствующие фактические параметры основной программы (!).
Пример:
var c: integer; procedure p(b: integer); {b – формальный параметр} begin … end; begin … p(c); { с – фактический параметр} end.
По способу описания и передачи значения формальные параметры делятся на параметры-значения и параметры-переменные.
Изменение значения таких параметров в подпрограмме не приводит к изменениям соответствующих фактических параметров основной программы. В подпрограмму передается копия фактического параметра (его значение). Поэтому фактические параметры, соответствующие параметрам-значениям, могут быть константами, переменными или выражениями. Другими словами, параметр-значение считается обычной локальной переменной. При вызове подпрограммы начальное значение параметра автоматически устанавливается равным значению соответствующего фактического параметра, заданного при вызове. Внутри подпрограммы возможны любые действия с формальными параметрами, но эти изменения никак не отражаются на значениях переменных основной программы.
Пример1:
Изменение формальных параметров в подпрограмме приводит к аналогичному изменению фактических параметров, используемых при вызове подпрограммы. Эти параметры используются для возврата значений из процедур (в функциях обычно не используются, т.к. функция возвращает значение через свое имя).
В подпрограмму передается адрес фактического параметра, а не его копия. (Формальный параметр считается синонимом соответствующего фактического параметра). Поэтому фактический параметр, соответствующий параметру-переменной, должен быть переменной того же типа, что и формальный параметр (константы или выражения не допускаются).
Пример 1:
Пример2: Программа вычисления объема шара V=4/3πR3
Побочные эффекты возникают, когда изменение переменных в подпрограмме приводит к их нежелательным изменениям в основной программе
Процедура изменяет значение глобальной переменной S (!). Надо добавить в нее описание локальной S:
procedure Sum(n: integer, var f: integer); var i: integer; s: integer; begin …
Типом формального параметра подпрограммы может быть стандартный или заранее определенный тип, заданный в виде идентификатора (!). Поэтому описание процедуры следующего вида является недопустимым:
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 являются необязательными для изучения, хотя рассмотренный в них материал достаточно широко используется в программировании.
В Турбо-Паскаль 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.
В Турбо-Паскаль 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;
В теле подпрограммы доступны все объекты, описанные в основной программе, в том числе и имя самой подпрограммы. Таким образом, внутри тела подпрограммы возможен вызов самой подпрограммы. Процедуры и функции, использующие вызовы “самих себя”, называются рекурсивными.
Многие математические алгоритмы имеют рекурсивную природу, поэтому рекурсия широко используется в программировании. В качестве примера приведем известный алгоритм вычисления факториала неотрицательного целого числа:
(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. |