учебник по паскалю. Программа 5 Алгоритм 5 Свойства алгоритма 6 Формы записи алгоритма 6
Скачать 2.21 Mb.
|
18.1. ПроцедурыОбщий вид подпрограммы-процедуры следующий: procedure Имя (Список формальных параметров); {выше - заголовок подпрограммы} var описания локальных переменных; begin {Тело процедуры} end; Другие подразделы раздела описаний, такие как label, const и изучаемый в п. 18.3 оператор 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), то соответствующий фактический параметр не может быть константой (ведь значение константы нельзя изменить). Для больше наглядности опишем данное соответствие в табл. 18.1. Табл. 18.1. Соответствие формальных и фактических параметров
О массивах как параметрах подпрограмм будет подробно рассказано ниже, а сейчас обсудим более подробно различие между "входными" и "выходными" параметрами процедур. На самом деле первые называются параметрами-значениями, а вторые -- параметрами-переменными. Передача процедуре "входных" параметров называется передачей по значению -- в этом случае при входе в процедуру для фактического параметра, которым может быть константа, переменная или значение выражения, создается временная локальная переменная, в которую и помещается вычисленное значение фактического параметра. При выполнении процедуры значение этой переменной может изменяться, однако, после выхода из процедуры все изменения будут потеряны и значение фактического параметра останется без изменения: 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 в заголовке процедуры подчеркивает, что параметр является переменной и может изменяться этой процедурой. Перейдем к примерам. 1. Для точки на плоскости с заданными координатами (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. При всей своей простоте, этот листинг иллюстрирует особенность любой грамотно составленной сложной программы: как правило, главная программа состоит лишь из вызовов подпрограмм, выполняющих всю необходимую работу. Кроме того, договорившись о параметрах процедур, такую программу мог составить и коллектив разработчиков. 2. Написать процедуру, печатающую первые 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. |