Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
основы программирования на Паскале.doc
Скачиваний:
260
Добавлен:
25.03.2016
Размер:
4.34 Mб
Скачать

18.1 Процедуры

Общий вид подпрограммы-процедуры следующий:

Procedure Имя (Список формальных параметров);

{выше - заголовок подпрограммы}

Var описания локальных переменных;

Begin

{Тело процедуры}

End;

Другие подразделы раздела описаний, такие как label,constи изучаемый ниже операторtype, также могут присутствовать между заголовком и телом процедуры и действие их также будетлокально– то есть, определено лишь в рамках данной процедуры. Единственная правильная связь процедуры с "внешним миром", то есть, другими подпрограммами и главной программой – указанный после именисписок формальных параметров. В этом списке через запятую указываются входные и выходные параметры процедуры с указанием их типов данных. Входные параметры служатисходными даннымидля процедуры, а выходные определяютрезультаты ее вычислений, передаваемые в главную программу или другую подпрограмму. Передвыходнымипараметрами, измененные значения которых должны сохраняться после завершения процедуры, следуетуказывать ключевое слово var. Причины именно такого указания мы обсудим ниже.

Само по себе написание процедуры еще не вызывает выполнения никаких действий. Чтобы процедура сработала, ее нужно вызвать, записав в нужной точке программы имя процедуры сосписком фактических параметров, которые будут подставлены на место формальных. Все это делается не так сложно, как звучит. Представим, что мы решили написать процедуру решения любого квадратного уравнения с именемEquation. Входными данными этой процедуры будут значения коэффициентов уравненияa,bиc, выходными – найденные корниx1иx2(если они существуют). Кроме того, нам понадобится "временная" (а точней, локальная) переменнаяd, позволяющая процедуре хранить вычисленное значение дискриминанта:

Procedure Equation (a,b,c:real; var x1,x2:real);

Var d:real;

Begin

d:=sqr(b)-4*a*c;

if d>=0 then begin

x1:=(-b+sqrt(d))/(2*a);

x2:=(-b-sqrt(d))/(2*a);

end;

End;

Поскольку все параметры этой процедуры – вещественные, в ее заголовке нам хватило двух списков – один для входных параметров a,bиc, другой – для выходныхx1иx2(обратите внимание на словоvarперед этим списком!). В случае отрицательного значения дискриминанта, значенияx1иx2 остаются неопределенными (что, вообще говоря, не совсем корректно!), в противном случае они вычисляются и должны быть переданы в главную программу. Вызвать эту процедуру мы могли бы, например, так:

Var a,b,c,d,x1,x2,x3,x4:real;

. . .

Write ('Введите значения a,b,c:');

Read (a,b,c);

Equation (a,b,c,x1,x2);

{ попытались решить уравнение ax2+bx+c=0,

значения a,b,c, ввел пользователь,

ответы, если они вычислялись, будут помещены в x1 и x2}

Equation (1,4,-3.5,x3,x4);

{ решили уравнение x2+4x-3.5=0, ответы поместили в x3 и x4}

Equation (4,1,-3.5,x3,x4);

{ попытались решить уравнение 4x2+x-3.5=0,

а ответы поместить в x1 и x2}

Equation (1,b,0,x1,x3);

{ попытались решить уравнение x2+bx=0,

а ответы поместить в x1 и x3}

. . .

И так далее. Суть в том, что при каждом вызове подпрограммы значения фактических параметров подставляются на место формальных и с ними производятся вычисления, предусмотренные операторами подпрограммы. Указанные требования называютсогласованием параметрови описывают следующим образом:формальные и фактические параметры должны быть согласованы между собой по количеству, типу и порядку следования. Это означает, что количество формальных и фактических параметров должно быть одинаковым, при этом, при вызове процедуры каждый фактический параметр должен иметь тот же тип и занимать в списке то же место, что и соответствующий ему формальный параметр. Из сказанного следует, что нашу процедуруEquationможно вызвать только с пятью параметрами (а не тремя, семью или нулём), причем все эти параметры должны быть вещественными. Если формальный параметр является выходным (перед ним в заголовке процедуры указано ключевое словоvar), то соответствующий фактический параметр не может быть константой (ведь значение константы нельзя изменить). Для больше наглядности опишем данное соответствие в виде таблицы:

Формальный параметр в заголовке процедуры

Соответствующий фактический параметр при вызове процедуры

Переменная некоторого типа данных без var

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

Переменная некоторого типа данных с var

Переменная или элемент массива того же типа данных

Массив

Массив

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

procedure p1 (x:integer); {для x создастся локальная копия!}

begin

x:=x+1; {значение копии увеличилось на 1}

writeln ('X=',x); {и было выведено на экран}

end;

var x:integer;

begin

x:=3;

p1(x);

writeln ('X=',x);

{после вызова с передачей параметра по значению,

x по-прежнему равно 3}

end.

Если формальный параметр процедуры помечен ключевым словом varкак параметр-переменная, это означает, что передача фактического параметра осуществляетсяпо адресуилипо ссылке– то есть, в процедуру передаетсяадрестого места в памяти, где находится фактический параметр. Таким образом, все выполняемые в процедуре изменения значения параметра-переменной будут выполнены непосредственно над значением фактического параметра. По той же причине формальному параметру-переменной может соответствоватьтолько фактический параметр-переменная– ведь значение константы нельзя изменить, а выражение не имеет конкретного адреса в памяти, вычисляясь каждый раз при выполнении программы:

procedure p1 (var x:integer);

{получаем адрес переменной, переданной в качестве x}

begin

x:=x+1; {значение x увеличилось на 1}

writeln ('X=',x); {и было выведено на экран}

end;

var x:integer;

begin

x:=3;

p1(x);

writeln ('X=',x);

{после вызова с передачей параметра по ссылке,

x стало равно 4 - оно было изменено процедурой p1}

end.

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

Перейдем к примерам.

Пр. Для точки на плоскости с заданными координатами (x,y) найти расстояниеlот точки до начала координат, а также длину окружности и площадь круга радиусомlс центром в начале координат.

Обозначим длину окружности как c, а площадь круга –s. Нетрудно заметить, что помимо выделения в отдельную процедуру расчета величинl,cиs, целесообразно написать также процедуруGetдля ввода вещественного значения с предварительным выводом приглашения к вводу (нам придется вводить, по меньшей мере, 2 значения –xиy), а также процедуруPutдля печати вещественного значения с указанной шириной, точностью и строкой комментария (выводиться будут искомые величиныl,cиs).

procedure Circle2 (x,y:real; var l,c,s:real);

const pi=3.1415926;

{Просто для иллюстрации; на самом деле есть функция pi}

begin

l:=sqrt(sqr(x)+sqr(y));

c:=2*pi*l;

s:=pi*sqr(l);

end;

procedure Get (S:string; var x:real);

{S - приглашение,

x - параметр-переменная, вводимая с клавиатуры величина}

begin

write (S,': ');

readln (x);

end;

procedure Put (S:string; x:real);

{S - комментарий к выводу, x - выводимая величина}

begin

writeln (S,'= ',x:8:3);

end;

var x,y,c,s,l:real;

begin

writeln;

Get ('Введите координату x',x);

Get ('Введите координату y',y);

Circle2 (x,y,l,c,s);

Put ('Удаление от начала координат',l);

Put ('Длина окружности',c);

Put ('Площадь круга',s);

end.

Несмотря на то, что процедура Circle2вызвана в этой программе однократно, она может пригодиться при повторном использовании кода. Исходными данными (параметрами-значениями) для этой процедуры являются координаты точкиxиy, выходными данными (параметры-переменные) – найденные значенияl,cиs.

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

Пр. Написать процедуру, печатающую первые N членов ряда Фибоначчи.

Члены ряда Фибоначчи вычисляются по формуле:

F0= 0, F1= 1, FN= FN-1+ FN-2, N=2,3,...

Заметим, что кроме простой и красивой формулы, ряд Фибоначчи замечателен еще тем, что отношение двух его соседних членов дает в пределе константу "золотого сечения", широко используемую в самых различных расчетах. Для вычисления и вывода на экран Nпервых членов ряда Фибоначчи напишем процедуруFiboс формальным параметромall, определяющим, сколько членов ряда требуется найти. В главной программе организуем цикл ввода значения фактического параметраN.

procedure Fibo (all:integer);

var n,n1,n2:longint;

{n – текущий элемент ряда,

n1 – предыдущий, n2 – вычисленный 2 шага назад

}

i:integer;

begin

n1:=1; n2:=0;

writeln;

write (n2,' ',n1,' ');

for i:=2 to all do begin

n:=n1+n2;

write (n,' ');

n2:=n1; n1:=n; {переприсвоили для следующего шага}

end;

end;

var n:integer;

begin

repeat

writeln ('Введите число членов ряда или 0 для выхода:');

read (n);

if n=0 then halt(1)

else Fibo(n);

until false;

end.