9797

Процедуры и функции. Блочная структура программы

Реферат

Информатика, кибернетика и программирование

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

Русский

2013-03-17

112.5 KB

22 чел.

Процедуры и функции. Блочная структура программы

Введение

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

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

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

Можно сказать, что понятие подпрограммы - элементарное средство повышения "уровня" языка. Сосредоточив в одном месте программы подробное описание некоторых "технических" аспектов вычислений, в остальной программе достаточно указывать имена этих действий (своего рода "операций", введенных программистом), не конкретизируя всякий раз их семантику.

Кроме задания последовательности действий, любая подпрограмма может содержать описания некоторой совокупности локальных объектов - констант, типов, переменных и т.д. Эти объекты предназначены для организации действий в подпрограмме и имеют смысл (то есть доступны или видимы) ТОЛЬКО ВНУТРИ данной подпрограммы.

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

Все сказанное прямо касается языка Pascal, в котором механизм подпрограмм (носящих название процедур и функций) выдержан в традиционном для алгоритмических языков стиле. Turbo Pascal сохраняет все достоинства авторской версии языка, в некоторых аспектах расширяя его.

Общая структура подпрограмм

Структура подпрограммы почти буквально повторяет структуру всей Pascal-программы, что имеет глубокий смысл (часть подобна целому) и подчеркивает "регулярный" характер построения языка.

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

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

• локальный контекст подпрограммы: совокупность описаний (рабочих) объектов, с которыми осуществляются действия;

• собственно действия (операторы), составляющие смысл подпрограммы.

Интерфейс подпрограммы или, иными словами, та информация, которая «представляет» подпрограмму и достаточна для корректного ее вызова, сосредоточена в заголовке. Описание локальных объектов и операторы (алгоритм) подпрограммы составляют внутреннюю ее часть и, как правило, имеют синтаксис блока. Можно сказать, что заголовок содержит информацию о том, ЧТО делает подпрограмма, а тело подпрограммы (блок) описывает, КАК она это делает.

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

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

Общая структура описания процедур и функций иллюстрируется следующими синтаксическими диаграммами:

Описание процедуры

Заголовок процедуры

Заголовок функции

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

procedure Swap( var X, Y : real );

procedure Stop;

function Max(a,b:byte):byte;

Тело подпрограммы. Области действия имен

Как уже было сказано, телом процедуры или функции, как правило, является блок. Структура блока содержит описания объектов и группу операторов и описана в ранее (см. лекцию ____).

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

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

Можно схематически изобразить структуру блоков некоторой Pascal-программы следующим образом:

Здесь буквой A обозначен самый внешний блок программы; В и Е - блоки подпрограмм, описанных во внешнем блоке А; С и D - блоки, вложенные в подпрограмму в (то есть описанные в ней).

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

1. Имена объектов, описанных в некотором блоке, считаются известными в пределах данного блока, ВКЛЮЧАЯ и все вложенные блоки.

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

3. Если в некотором блоке описан объект, имя которого совпадает с именем объекта, описанного в объемлющем блоке, то это последнее имя становится недоступным в данном блоке. Говорят, что имя, описанное в блоке, экранирует (закрывает, делает недоступным) одноименные объекты из блоков, объемлющих данный.

Возвращаясь к предыдущей схеме, можно сказать, что объекты, описанные в блоке В, известны (видимы), кроме самого блока в, еще и в блоках А и Е. Имена из блоков С, D, E известны только в пределах соответствующих блоков. Наконец, имена объектов из самого внешнего блока А (то есть блока, соответствующего программе в целом) известны во всех вложенных блоках.

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

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

Такие современные языки, как Ada, сохраняя общие принципы блочности, имеют более гибкие и развитые правила видимости имен. Turbo Pascal также несколько расширяет эти правила введением новых для языка Pascal понятий модулей (units) и объектов (objects). В соответствующих разделах описанные принципы блочности будут уточнены применительно к этим понятиям.

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

program Example;

var

InFile : file of integer; { исходный файл }

{ переменные для подсчета нужных значений }

CountPos, CountNeg : word;

SumPos, SumNeg : longint;

procedure Start; { начальные действия }

begin

Assign(InFile,ParamStr(1));

Reset(InFile);

CountPos:=0; CountNeg:=0;

SumPos:=0; SumNeg:=0

end;

procedure Process; { вычисления }

var Val : integer;

{ вложенные процедуры }

procedure ProcessPos ( Item:integer ) ;

{ подсчет положительных чисел }

begin

 SumPos := SumPos+Item;

 inc(CountPos)

end;

procedure ProcessNeg (Item:integer);

{ подсчет отрицательных чисел }

begin

 SumNeg := SumNeg+Item;

 inc(CountNeg)

end;

begin { Process }

repeat

Read(InFile,Val); if Val > 0 then

ProcessPos(Val) else

ProcessNeg(Val) until

Eof(InFile)

end; { Process }

procedure Finish; { завершающие действия }

begin

Writeln('Сумма',CountPos,

    'положительных чисел =',SumPos);

Writeln('Сумма',CountNeg,

'отрицательных чисел =',SumNeg);

Close(InFile)

end;

begin { начало внешнего блока программы }

Start;

Process;

Finish

end. { конец программы }

Программа содержит три процедуры, каждая из которых решает одну частную подзадачу:

• открытие необходимого файла с записанными в него числами и инициализация глобальных переменных;

• собственно вычисление сумм и количества чисел;

• вывод результатов и закрытие файла.

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

Глобальный контекст программы образуют имена четырех переменных, предназначенных для подсчета результатов, имя файла с исходными данными, а также имена трех процедур (Start, Process, Finish). Все эти имена считаются известными и используются во всех блоках программы.

Собственный контекст процедуры Process включаег имя рабочей переменной Val и имена двух вложенных процедур ProcessPos и ProcessNeg. Эти процедуры используются только внутри процедуры Process, поэтому внесение их в ее контекст (и, тем самым, их изоляция от доступа из других блоков) является оправданной.

Блоки процедур Start и Finish не имеют разделов описаний и списка параметров, следовательно, не имеют собственного контекста имен. Они работают с глобальными переменными, которые доступны им по правилам блочности.

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

Механизм параметров

В заголовке процедуры или функции может быть задан список формальных параметров. Этот список указывается после имени подпрограммы и заключается в круглые скобки. Описание формальных параметров очень похоже на описание переменных в блоке. Каждый параметр, заданный в заголовке, считается локальным в данной подпрограмме так же, как и переменные, описанные в блоке этой подпрограммы.

Идентификаторы формальных параметров можно считать условными обозначениями в теле подпрограммы тех реальных или ФАКТИЧЕСКИХ параметров, которые будут переданы в подпрограмму при ее вызове.

Ниже приводятся синтаксические диаграммы для формальных параметров.

Список формальных параметров

Описание параметров

Тип параметра

Примеры заголовков процедур и функций:

function MaxElem ( A:Vector; n:byte ) : real;

procedure Sum ( А, В : Matrix; var С : Matrix );

procedure Move ( var Source, Dest; n : word );

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

procedure Incorrect ( var А : array[1..10] of byte );

Если необходимо передать в подпрограмму параметр с типом, определяемым программистом, следует воспользоваться его идентификатором, например:

type

MyArray = array[1..10] of byte;

. . .

procedure Correct ( var A : MyArray ) ;

. . .

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

Далее, из приведенных выше синтаксических диаграмм следует, что в списке формальных параметров подпрограмм допустимы по крайней мере три способа их задания:

1. Параметры, перед которыми отсутствует служебное слово var и за которыми следует идентификатор типа.

2. Параметры, перед которыми указано служебное слово var и за которыми следует тип.

3. Параметры, предшествуемые служебным словом var и не имеющие типа вслед за ними.

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

Первые два способа передачи параметров (эти параметры также называются параметрами-значениями и параметрами-переменными) были определены в авторской версии языка Pascal, третий способ имеется только в Turbo Pascal и представляет некоторое отступление от принципов строгой типизации, характерных для языка. Этот способ будет описан далее.

Параметры-значения

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

Внутри подпрограммы возможны произвольные действия с данным формальным параметром, но любые изменения его значения НИКАК НЕ ОТРАЖАЮТСЯ на значениях переменных вне подпрограммы.

Ниже приводится пример процедуры, использующей передачу параметров по значению.

procedure SumSquare (X, Y : real ) ;

begin

X := X*X;

Y := Y*Y;

Writeln( ‘Сумма квадратов = ‘,X+Y)

end;

Вызов этой процедуры может выглядеть следующим образом:

var

А, В : real;

begin

А := 1.7;

В := 8.09;

SumSquare(А,В);

. . .

При вызове этой процедуры с фактическими параметрами А и В значения этих параметров (один раз) копируются в соответствующие формальные параметры X и Y, и дальнейшие манипуляции с формальными параметрами внутри процедуры SumSquare никак не влияют на значения переменных А и В.

Параметры–переменные

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

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

procedure Draft1 ( X, Y : real );

var Sum, Sub : real;

begin

Sum := X*X + Y*Y;

Sub := X*X - Y*Y

end;

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

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

procedure Draft2 ( X, Y : real; Sum, Sub : real );

begin

Sum := X*X + Y*Y;

Sub := X*X - Y*Y

end;

Что произойдет при вызове данной процедуры, например, в следующем фрагменте?

var  А, В : real;

SumAB, SubAB : real;

begin

A := 1.7;

В := 8.09;

Draft2(A, B, SumAB, SubAB);

. . .

В соответствии с правилами языка параметры Sum и Sub в процедуре Draft2, передаваемые по значению, являются локальными в пределах этой процедуры, поэтому присваивание им любых значений НЕ ПРИВЕДЕТ к получению этих значений переменными SumAB и SubAB.

Для того, что изменение в теле процедуры значения формального параметра ПРИВОДИЛО К АНАЛОГИЧНОМУ ИЗМЕНЕНИЮ соответствующего фактического параметра, и необходимо использовать передачу параметра по ссылке:

procedure SumSub ( X, Y : real; var Sum, Sub : real );

begin

Sum := X*X + Y*Y;

Sub := X*X - Y*Y

end;

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

Если вызов процедуры SumSub будет выглядеть следующим образом:

var

А, В : real;

SumAB, SubAB : real;

begin

A := 1.7;

В := 8.09;

SumSub(A, B, SumAB, SubAB);

. . .

то присваивания параметрам Sum и Sub внутри тела процедуры будут означать соответствующие присваивания переменным SumAB и SubAB, переданными процедуре как параметры-переменные. После завершения процедуры эти переменные будут содержать искомые значения.

Приведем еще один пример передачи параметров-переменных. Часто бывает необходимо произвести обмен значений между двумя переменными. Эти действия можно оформить в виде следующей процедуры:

procedure Swap ( var X, Y : real );

var

Т : real;

begin

Т := X;

X := Y;

Y := Т

end;

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

Swap(А, В)

присваивания параметрам-переменным X и Y в теле процедуры означают присваивания соответствующим фактическим параметрам А и В.

Следует иметь в виду, что переменные файловых типов могут передаваться в подпрограммы только как параметры-переменные.

Бестиповые параметры

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

var Ident,

где Ident - идентификатор формального параметра.

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

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

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

function Equal ( var Source, Dest; Size : word) : boolean;

type

Bytes = array[0..Maxlnt] of byte;

var

N : integer;

begin

N := 0;

while ( N < Size ) and

( Bytes(Dest)[N] <> Byres(Source)[N] )

do N := N + l;

Equal := (N=Size)

end;

Если имеются следующие описания:

type

Vector = array[1..10] of integer;

Point = record

  x, у : integer

end;

var

Vecl, Vec2: Vector;

N : integer;

P : Point;

то следующие вызовы этой функции:

Equal(Vecl,Vec2,SizeOf(Vector))

Equal(Vecl,Vec2,SizeOf(integer)*N) Equal(Vec[1],Vec1[6],SizeOf(integer)*5)

Equal(Vec1[1],P,4)

будут выполнять такие действия: в первом примере сравнивается все элементы массивов Vec1 и Vec2; второй вызов сравнивает первые N элементов массива Vec1 с первыми N элементами массива Vec2; в третьем примере сравниваются первые 5 элементов Vec1 с последними пятью элементами Vec2. Наконец, последний вызов сравнивает элемент массива Vec1[1] с P.x и элемент Vec2 [2] с Р.у.

Второй пример схематически показывает задание различных действий в зависимости от размера переданного нетипизированного параметра.

procedure DoSomething ( var X );

var

V1 : byte absolute X;

V2 : word absolute X;

V3 : longint absolute X;

begin

case SizeOf(X) of

  SizeOf(byte) : begin ... V1 ... end;

  SizeOf(word) : begin ... V2 ... end;

  SizeOf(longint) : begin ... V3 ... end

end

end;

Локальными переменными процедуры являются три переменные различных типов, расположенные в той же области памяти, что и параметр X. Таким образом, в зависимости от своего размера параметр X трактуется как переменная типа byte (в этом случае его "синонимом" является локальная переменная V1), либо как имеющий тип word (переменная V2), либо как длинное целое (переменная V3).

Вычисление значения функции. Завершение подпрограмм

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

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

Следующий пример иллюстрирует сказанное.

function Мах ( А, В : integer ) : integer;

{ Определение максимума двух целых чисел }

begin

if A > В then

  Мах := А

else

  Мах := В

end;

Функция Мах может быть использована в выражении, например:

М := Мах(А-В,А+В) + 2*Мах(А*В,А mod В);

Работа процедуры или функции по определению завершается после выполнения последнего оператора ее тела. Pascal не содержит специальных языковых средств для «преждевременного» завершения подпрограммы (типа оператора RETURN в PL/I). Однако Turbo Pascal имеет дополнительное средство прерывания выполнения подпрограммы в произвольной точке. Для этого предназначена системная процедура Exit без параметров, которая немедленно завершает выполнение подпрограммы и возвращает управление в точку вызова. (Необходимо только помнить, что перед вызовом Exit в теле функции должен обязательно выполниться оператор присваивания с идентификатором функции в левой части).

Следующий пример показывает использование системной процедуры Exit.

procedure P ( X, Y : real; var Res : real ) ;

begin

if X-Y < 0.0001 then Exit;

Res := (X*X+Y*Y)/(X*X-Y*Y)

end;

Если вызов процедуры Exit был произведен из блока, соответствующего программе в целом (то есть из самого внешнего блока), то будет немедленно завершено выполнение всей программы.

Предварительные и внешние описания подпрограмм

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

Достаточно распространенным является случай, когда две подпрограммы, описанные "рядом" (то есть на одном уровне вложенности) содержат взаимные вызовы друг друга, например:

procedure John ( х, у : real ) ;

begin

Jack(1,2)

end;

procedure Jack ( a, b : integer );

begin

John(0.5,0.6)

end;

При трансляции процедуры John компилятор не может правильно обработать вызов процедуры Jack, так как эта процедура описывается ниже по тексту программы и информация о ней еще неизвестна. Если поменять местами эти две процедуры, то аналогичная проблема возникнет с трансляцией вызова John в процедуре Jack.

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

procedure John ( х, у : real ) ; forward;

procedure Jack ( а, b : integer ); forward;

procedure John;

begin

Jack(1,2)

end;

procedure Jack;

begin

John(0.5,0.6)

end;

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

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

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

В Pascal-программе необходимо указать заголовок подключаемой подпрограммы, после которого (вместо тела подпрограммы) должно следовать служебное слово external. Кроме того, где-либо в тексте программы необходимо задать директиву компилятора $L, аргументом которой является имя OBJ-файла, содержащего код подключаемой подпрограммы, например:

procedure SqRoots(А,В,С:real); external;

procedure CbRoots(A,B,C,D:real); external;

. . .

{$L ROOTS.OBJ)

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

Рекурсия и побочный эффект

В теле подпрограммы известны (доступны) все объекты, описанные в объемлющем блоке, В ТОМ ЧИСЛЕ и имя самой подпрограммы. Таким образом, внутри тела подпрограммы возможен вызов самой подпрограммы. Процедуры и функции, использующие вызовы "самих себя", называются рекурсивными. Допустима также косвенная рекурсия, при которой, например, процедура А вызывает процедуру В, та, в свою очередь, вызывает C, которая вызывает первоначальную процедуру А.

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

0! = 1

1! = 1

N! = 1*2* ... *(N-1)*N

Алгоритм основан на очевидном соотношении

N! = (N-1)! * N ,

что позволяет для вычисления факториала использовать результат точно такого же вычисления для предыдущего числа.

function Fact ( N:word ) : longint;

begin

if N = 1 then

  Fact := 1

else

  Fact := N*Fact(N-l)

end;

В языке Pascal нет никаких ограничений на рекурсивные вызовы подпрограмм, необходимо только хорошо понимать, что каждый очередной рекурсивный вызов приводит к образованию НОВОЙ копии локальных объектов подпрограммы и все эти копии, соответствующие цепочке активизированных и не завершенных рекурсивных вызовов, существуют независимо друг от друга.

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

Если в некоторой функции имеются конструкции (например, операторы присваивания), изменяющие значения переменных, описанных в объемлющих блоках, то может возникнуть ситуация, при которой значение выражения, использующего вызов такой функции, ЗАВИСИТ от порядка следования операндов, что является потенциальным источником трудноуловимых программных ошибок и поэтому крайне нежелательно. Описанная ситуация называется побочным эффектом. Примером побочного эффекта может служить следующая программа:

program SideEffect;

var

a, z : integer,

function Change ( x:integer ) : integer;

begin

{ изменяем значение нелокальной переменной }

z : = z - x;

Change := sqr(x)

end;

begin

z := 10; a ;= Change(z); Writeln(a,z);

z := 10; a := Change(10)*Change(z); Writeln(a,z) ;

z := 10; a := Change(z)*Change(10); Writeln(a,z)

end.

Выполнение этой программы приведет к записи на стандартный выводной файл (на дисплей) следующих значений:

100 0

10000 -10

0 0

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

Следует всячески избегать такой зависимости функций от глобальных по отношению к ней переменных. Заметим, что современные языки (например, Ada) содержат прямые запреты на подобные действия.


 

А также другие работы, которые могут Вас заинтересовать

54214. Нахождение неизвестного делителя. Задачи в два действия. Составление и решение выражений 45.5 KB
  Неизвестное число разделили на 6 и получили 5. Найдите неизвестное число. Неизвестное число уменьшили на 7 и получили 89. Чему равно неизвестное число Неизвестное число уменьшили в 6 раз и получили 7.
54216. Ділення багатоцифрових чисел на трицифрові, коли частка містить нулі 176 KB
  Мета: вчити учнів письмовому діленню багатоцифрових чисел на трицифрові, коли в частці є нулі; удосконалювати знання і навички учнів у розв’язані задач; розвивати обчислювальні навички, логічні мислення, кругозір учнів, уяву, спостережливість, раціональність думки; виховувати інтерес до математики, охайність в записах, бережливе ставлення до природи.
54217. Культура Древнего Египта и ее основные особенности 16.48 KB
  На северо-востоке Африки находится родина древнейшей в мире цивилизации – Египет. 4 – 3-м тысячелетии до н.э., когда обитавшие в Средней Европе варварские племена еще носили звериные шкуры и жили в пещерах...
54218. Узагальнення з теми «Числові та буквені вирази. Формули. Рівняння» 50.5 KB
  Питання для першої команди: Що значить розв’язати рівняння Сформулюйте властивість віднімання суми від числа. Сформулюйте властивість нуля при додаванні. Як знайти невідомий від’ємник Як перевірити чи вірно розв’язано рівняння Питання для другої команди: Який вираз називають буквеним Сформулюйте властивість нуля при відніманні. Як знайти невідомий доданок Сформулюйте сполучну властивість додавання.
54219. Десятичные дроби. Урок-соревнование в 5-м классе 35.5 KB
  Оборудование: Компьютер, мультимедийный проектор, карточки для деления класса на команды, жетоны для оценивания ответов, колокольчики, призы победителям.
54221. Числові та буквені вирази. Формули. Рівняння 48.5 KB
  Рівняння. Розвивати навички застосовувати теоретичні знання на практицірозв’язувати складні рівняння та складати рівняння за умовою задач. Якір №507 Два учня записують розв’язання рівняння на дошці: 1 х23:9=13 2 1728:56х=36 х23=139 56х=1728:36 х23=117 56х=48 х=11723...
54222. Розвязування вправ на всі дії з натуральними числами 70 KB
  Мета: закріпити в учнів уміння виконувати дії над натуральними числами в процесі розв’язування різноманітних вправ; сприяти розвитку логічного мислення обчислювальних навичок учнів культуру математичної мови і записів; формувати інтерес до математики; виховувати самостійність наполегливістьвзаємодовіру. Після уроку учні зможуть: узагальнити і систематизувати свої знання про натуральні числа; додавати віднімати множити й ділити натуральні числа; розв’язувати рівняння на основі...