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

Идентичность типов

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

Type Int = Integer;

Var n : Int; k : Integer;

vl, v2 : Array[1..10] of Real;

В данном случае vl и v2 идентичны, пик идентичны, но если объявле-Ho:Var vl : Array[1..10] of Real; v2 : Array[1..10] of Real;, то vl и v2 не идентичны.

Совместимость типов

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

  • типы идентичны;

  • один тип представляет тип-диапазон другого;

  • для объявления используется один и тот же базовый тип;

  • оба типа - целые типы;

  • оба типа - вещественные типы;

  • один тип - строка типа PChar, другой - массив символов с индексацией элементов, начинающейся от нуля.

СОВМЕСТИМОСТЬ ПО ПРИСВАИВАНИЮ

Чтобы присваивание было корректным, необходимо выполнение сле­дующих условий:

  • идентичность типов;

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

  • слева от знака присваивания - вещественный тип, а справа - целый тип;

  • слева - строка, кроме типа PChar, справа - любая строка;

86

• слева - строка, кроме типа PChar, справа - символ.

Преобразование типов

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

Type ByteRec = record

Lo, Hi:byte; End;

WordRec = record

Lo, Hi:word; End;

Var B:byte; W:word; L:LongInt;,

TO МОЖНО записать:

L:=LongInt(W> ; В: =ByteRec(W) .Lo;

ByteRec(W) .Hi:=0;

W:=WordRec(L) .Lo; B:=ByteRec(WordRec(L).Lo).Hi;.

К специальным функциям и процедурам преобразования типов относит­ся множество подпрограмм, которые были использованы в программах вы­ше (Ord, IntToStr, StrToInt, Chr, FloatToStr, и др.).

Операторы обработки исключительных ситуаций

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

1-Выполнение программы дойдет до операторов локальной обработки исключительной ситуации.

2. Управление будет передано встроенному глобальному обработчику исключительной ситуации.

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

87

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

В любом приложении, основанном на VCL, имеется глобальная пере-менная Application, являющаяся экземпляром класса TAppIication. Этод класс определен в модуле Forms и объединяет в себе все автоматически соз­дающиеся формы программы. Объект Application выполняет множество функций. Одна из них - обеспечение по умолчанию глобального механизма обработки исключительных ситуаций. Как только произойдет исключительная ситуация, объект Application генерирует событие OnException. Событию TApplication.OnException можно назначить собственный обработчик. Этот процесс называется делегированием событий. Если никакая процедура не назначена, то событие OnException вызывает стандартный обработчик.

Наряду с механизмом делегирования программную обработку ошибок можно осуществлять с помощью специальных операторов. Для этих целей определены две конструкции: Try...finally и Try...except. Они совершенно аналогичны по синтаксису, но различаются по назначению. Конструкция Try...finally имеет следующий синтаксис: Try <операторы> finally <операторы> End;.

Если в любом из операторов, размещенных между Try...finally, возни­кает исключительная ситуация, их выполнение прекращается и управление передается первому оператору, следующему за ключевым словом finally. После этого выполняются все операторы, стоящие между finally и End (опе­раторы, стоящие между finally и end, выполняются и в том случае, если ошибки не произошло).

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

Конструкция Try...except применяется для перехвата исключительной ситуации с последующей возможной обработкой, предусматривающей осво­бождение экземпляра объекта обработки исключительной ситуации, после чего выполнение программы продолжается, что и отличает, главным обра­зом, оператор Try...except от Try...finally. В этой конструкции есть необя­зательный дополнительный элемент, с помощью которого можно определить фактический тип возникшей исключительной ситуации, - элемент вида on...do:

Try

<операторы>

except

on В: Exception do <опвратор>

on E: Exception do <оператор>

<оператор>

End;

После ключевого слова else часто следует ключевое слово Raise, кото­рое переводит обработку ошибки на новый уровень (разрешается создавать вложенные блоки Try), например, на уровень глобального обработчика. Raise прерывает программу и выталкивает данные из стека. Однако, если будет записано Raise <процедура>, где процедура позволяет создать соб­ственное исключение, то выталкивания данных из стека не последует (если только внутри процедуры не возникнет ошибки). Процедура, записываемая после Raise, должна быть особого вида - она должна быть конструктором, так как она должна создать объект какого-либо класса исключений.

В заключение отметим, что блок от Try до Finally (или до Except) на­зывается блоком защиты ресурсов, а последующий блок до End - блоком очистки.

ПРИМЕР ПРИЛОЖЕНИЯ 11

Ап(Х). Вариант решения задачи приво-



Рис. 31



Построить график функции Y дитсянарис. 31.

89

Методика построения графиков приводится при описании примера 4. Однако данная функция обладает рядом особенностей, которые удобно ис­пользовать для иллюстрации исключений. Во-первых, функция не определе­на при отрицательных значениях аргумента. Во-вторых, она не определена при Х= 1 (деление на нуль). Для всех особых точек, естественно, отображать график не будем.

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

класс ErangeError=class (EmathError) ; - являющийся наследником встроенного класса EmathError. Используя ключевое слово Raise, созда­дим два исключения: одно - ErangeError - для фиксации случая отрица­тельного аргумента, а другое - переопределим стандартное EDivByZero. Если в программе встретятся другие исключения, то они будут обрабаты­ваться стандартно - в программе эти ситуации определяются просто ключе­вым словом Raise. Программа для решения примера 11 приводится ниже.

unit primll; interface

uses Windows, Messages, SysUtils, Classes, Graphics,

Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls; type

TForm1 = class(TForm)

Panel1: TPanel;

Button1: TButton;

BitBtnl: TBitBtn;

Edit1: TEdit;

Label1: TLabel;

Edit2: TEdit;

Label2: TLabel;

Imagel: TImage;

Label3: TLabel;

procedure Edit1KeyPress(Sender: TObject; var Key: Char); procedure Button1Click(Sender: TObject); end;

ERangeError = class(EMathError); var Form1: TForm1 ;

implementation

{$R *.DFM}

procedure TForm1.Edit1KeyPress(Sender: TObject;

var Key: Char); begin

if not (key in ['0'..'9','-','. \#8]) then key:=#0; end;

procedure TForm1.Button1Click(Sender: TObject); var a,b:real;

xn,xk,yO,yk:integer;

90

mx,my:real; х,у,ymax:real;

i:integer;

lsDraw:boolean; {Для переключений при рисовании} begin

xn:=15;

xk:=280;

yO:=75;

yk:=7 0;

a:=StrToFloat(Edit1.Text); b:=StrToFloat(Edit2.Text); ymax:=10; {Задано произвольно}

IsDraw:=true;

With Imagel.Canvas do begin Pen.Color:=clBlack; Pen.Width:=1; Brush.Color:=clWhite;

Rectangle(0,0,Imagel.Width,Imagel.Height); MoveTo(xn,yO);

LineTo(xk,yO);

MoveTo(xn,yO+yk);

LineTo(xn,yO-yk);

End;

if b<=a then begin

Label3.Caption:= 'Графика нет';

Exit ;

End;

Imagel.Canvas.Pen.Color:=clRed; Imagel.Canvas.Pen.Width:=2; mx:=(b-a) / (xk-xn) ; ray:=yk/ymax;

for i:=0 to xk-xn do begin x:=a+i*mx; try

if x < 0 then Raise ERangeError.CreateFmt ('Шаг i= %d. Отрицательный диапазон,[i]); if (x > l-0.5*mx) and (x < l+0.5*mx) then Raise EDivByZero.CreateFmt

('Шаг i= %d. Деление на нуль,[i]); y:=my/ln(x); if IsDraw then begin

Imagel.Canvas.MoveTo(xn+i,yO-round(y)); IsDraw:=false;

end else Imagel.Canvas.LineTo(xn+i,yO-round(y)); except

on E:ERangeError do begin

MessageDlg(E.Message,mtWarning,[mbOK],0); IsDraw:=true;

end;

on E:EDivByZero do begin Application.MessageBox(PChar(E.Message),

'Предупреждение',mb_OK); IsDraw:=true; end else Raise; end; end;

end;

end.

В программе для создания объектов исключений используется встроен­ный конструктор createnat. В качестве аргументов в этот конструктор пе­редается строка, используемая для форматированного вывода (символ "%" представляет спецификатор формата, а последующий символ "d" - тип фор­мата - вывод цифровых значений), и конструктор открытого массива, в котором в квадратных скобках указываются отдельные элементы вывода.

Выполнять программу в данном случае необходимо не из среды Delphi, a вызывая соответствующий ЕХЕ-файл, иначе будут сгенерированы дополни­тельные исключительные ситуации, связанные с информацией отладчика.

МНОЖЕСТВА

Тип множество задает неупорядоченную совокупность неповторяю­щихся объектов. Переменная типа множество - это совокупность объектов из исходного заданного множества. Может иметь значение "пусто" (пустое множество). Число элементов исходного множества ограничено - оно не может быть более 256. Для задания элементов множества может использо­ваться любой порядковый тип, однако порядковые номера элементов мно­жества, т.е. значения функции ORD, должны находиться в пределах от 0 до 255. Для задания типа множества используется следующее объявление: туре <имя> = set of огип элементов;»;. При объявлении типа элементов необходимо соблюдать ограничения, указанные выше. Например,

Туре А = set of Char; Al = set of 'A' ..'Z' ;

Oper = set of {Plus, Minus, Malt, Divide); Number = set of Byte; D = set of 1..20;.

Переменные типа множество можно инициализировать с помощью ти­пизированных констант. При этом значения задаются в виде конструктор* множества Const K:D = [5,9,11,17]; R:D » [1..9,13,20] ;.ДО задания переменным текущих значений также можно использовать консТ'

92

рукторы. Пусть объявлено Var AA: а; , тогда возможна запись (тип А объ­явлен выше)АА:=[Char(13),Char(7),'0',*Q'];.

Если требуется присвоить этой переменной значение «пусто», то ис­пользуется такой конструктор: аа : = [ ];.

ОПЕРАЦИИ НАД МНОЖЕСТВАМИ

Как и для других типов, имеется ряд встроенных операций для типа множество. Пусть заданы следующие типы и переменные: туре мп = set of 1..50; Var А, в, с : Mn; и пусть переменным присвоены значе­ния: А := [3,5,9,10]; в := [1,7,9,10];, тогда можно записать сле­дующие операции (в фигурных скобках указан результат выполнения опера­ции):

  • объединение множеств: С:=А+В; - {1,3,5,7,9,10};

  • разность (а-в <> в-а): С:=а-В; - {3,5}, С:=в-а,-- {1,7};

  • пересечение (умножение): С:=А*В; - {9,10};

  • проверка эквивалентности (например, в операторе IF): (a=b) -{False};

  • проверка неэквивалентности: (А о в) - {True};

  • проверка, является ли одно множество подмножеством другого: (А>=в) - {False}, (A<=B) - {False};

  • проверка, входит ли заданный элемент в заданное множество: (3 in А) - {True}, (3 in В) - {False};

  • стандартные подпрограммы для выполнения некоторых действий над множествами: Exclude (А, 3) ; - удалить из множества а элемент 3, include (А, 3); - вставить элемент 3 во множество а.

Рассмотрим следующую задачу. Пусть с помощью генератора случай­ных чисел требуется выбрать 30 элементов из диапазона 0...200 и вывести их на экран дисплея в порядке возрастания. Пусть вывод осуществляется с помощью компонента ТМето. Ниже запишем код, который будет использо­ваться далее в примере 12.

Const к=30;

L=200;

Type Mn=set of 0.-L; Var S: Mn;

Str:String; i,Z:byte; Begin

Randomize;

Str:='';

Memol.Lines.Clear;

i:=l to К do While true do begin

Z:= random(L+l); If Z in S then continue; Include (S, Z) ; Break; End; For i:=0 to L do begin

If i in S then Str:=Str+IntToStr(i)+'

If (i mod 20=0) and (length{trim(Str)}>0) then begin Memol.Lines.Add{Str+' '); Str:=' л; End; End; End;.

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

ПРИМЕР ПРИЛОЖЕНИЯ 12

Пусть требуется сформировать с помощью генератора случайных чисел три множества: первое должно обязательно содержать элемент 25, второе -элемент 50, третье - элемент 100. Диапазон элементов - от 0 до 200 (исход­ное множество). Каждое множество должно состоять из 30 элементов. Вы­вести элементы этого множества в порядке возрастания, используя новый компонент TTabControl (на странице Win32). Компонент TTabControl представляет собой записную книжку (рис. 32), состоящую из вкладок и клиентской области. Вкладки, необходимые для листания страниц, распола­гаются вдоль верхней границы клиентской области. Встроенных страниц элемент не имеет, их нужно программировать самостоятельно с помощью свойства Tabs - в Object Inspector нажать на "..." и в редакторе напечатать столько строк, сколько требуется вкладок. Нумерация начинается с нуля и номер активной на данный момент страницы содержится в свойстве Tabln-dex. Свойства TabWidth и TabHeight содержат размер вкладок.

Управление данным компонентом осуществляется с помощью события OnChange. Это событие происходит, когда мышью будет выбрана какая-либо вкладка. Для того чтобы выводить какую-либо информацию в клиент­скую область вкладки, необходимо использовать 2 свойства: Handle (деск­риптор) и DispIayRect (задает прямоугольник клиентской области).

94

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

Рис- 32

Программа приведена ниже.

unit priml2;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Forms,

Controls, Dialogs, StdCtrls, Buttons, ComCtrls, ExtCtrls; type

TMainForm = class(TForm) TabControll: TTabControl; Bevel1: TBevel;

Panel1: TPanel;

Button1: TButton;

BitBtnl: TBitBtn;

Label2: TLabel;

procedure Button1Click(Sender: TObject); procedure TabContxollChange(Sender: TObject); end;

const k=30; L=200; mn=set of 0..L;

95

var MainForm: TMainForm;

s:array [0..2] of mn;

ss:array [0..2] of mn=([25],[50],[100]);

implementation

{$R *.DFM}

procedure TMainForm.Button1Click(Sender: TObject);

var i,j,z:byte; begin

randomize;

for j :=0 to 2 do

repeat

s[j] :=[]; for i: = l to к do

while true do begin z:=random(L+l) ; if z in s[j] then continue;

include(s[j],z);

break;

end;

until ss[j]<=s[j];

end;

procedure TMainForm.TabControllChange{Sender: TObject); var Canvas:TCanvas; str:string; i,j:byte;

begin

try

Canvas:=TCanvas.Create; try

Canvas.Handle:=GetDC((Sender as TTabControl).Handle); with (Sender as TTabControl) do begin Canvas.Brush.Color:=clWhite; Canvas.FillRect(DisplayRect); Canvas.Font.Color:=clRed;

str: = "; j:=0;

for i:=0 to L do begin

if i in s[TabIndex] then str:=str+IntToStr(i)+' ';

if (i mod 20 = 0) and (length(trim(str))>0) then begin

with DisplayRect do Canvas.TextOut(left+

Font.Size,top-j*font.Heightsj,str);

str:=’';

inc(j);

end;

end;

end;

finally

ReleaseDC((Sender as TTabCotrol).Handle,Canvas.Handle);

end; finally

Canvas.free; end; end; end.

Для создания канвы в клиентской области компонента TTabControl объявлена переменная canvas типа TCanvas. Создание экземпляра данного типа осуществляется с помощью процедуры create (эта процедура является конструктором). После выполнения определенных действий следует освобо­дить занимаемую канвой память, когда канва не будет нужна, с помощью процедуры Free, называемой деструктором. Вывод на канву, в данном слу­чае, текста осуществляется с помощью процедуры TextOut.

Работа с динамической памятью требует контроля возникновения воз­можных исключительных ситуаций. В программе это осуществляется с по­мощью двух вложенных операторов try...f inally...end. Как правило, объек­ты, которые автоматически создаются средой Delphi, автоматически и унич­тожаются. В данном примере мы создали свой объект canvas и поэтому должны сами его уничтожить. Первая конструкция try...finally отслежи­вает создание объекта Canvas, а вторая отслеживает подключение canvas к TTabCantrol. Соответственно после ключевых слов Finally высвобожда­ется занятая функцией GetDC и канвой память.

Рассмотрим, почему использовалась операция as. Sender - это пара­метр, с помощью которого в обработчик событий передается конкретный экземпляр объекта. Операция as - это подсказка компилятору, какого типа передаваемый объект. В данном случае это объект типа TTabControl.

ВАРИАНТНЫЙ ТИП ДАННЫХ

Этот тип данных объявляется с помощью ключевого слова Variant. Данные типа Variant представляют собой такие величины, тип которых либо неизвестен к началу работы программы, либо может изменяться в процессе ее выполнения. Они являются довольно гибким средством, но требуют до­полнительной памяти и работа с ними происходит медленнее, чем с другими типами. Поэтому использовать их следует только там, где это необходимо, например, при работе с OLE-объектами, программировании серверов и т.д.

Одна переменная вариантного типа может принимать значения различ- типов, например объявлено: var V: variant; D: Double; S:

96

97

string; W: Word;. Можно записать: D:= 6.88; V:= D; w: = V:= W; S:='BM РХТУ'; V:= S;.

Вариантный тип данных представляет собой запись и определен в моду ле System стандартным типом TVarData:

TvarData = record Vtype:word; Reservedl, Reserved2, Case Integer of

VarSmallint: (VSmallint:Smallint);

Varlnteger: {VInteger: Integer);

VarSingle: (VSingle: Single);

VarDouble: (VDouble: Double):

VarCurrency: (VCurrency: Currency)

VarDate: (VDate:Double);

VarOleStr: (VOleStr:PWideChar);

VarDispath: (VDispath:Pointer);

VarError: (VError:Integer);

VarBoolean: (VBoolean:WordBool);

VarUnknown: (VUnknown:Pointer);

VarByte: (VByte:Byte);

VarString: (VString:Pointer);

VarAny: (VAny:Pointer);

VarArray: (VArray:PVarArray);

VarByRef: (VPointer:Pointer); End; .

Reserved3:word;

Этот тип представляет собой запись с вариантной частью. Поле VType определяет конкретный текущий тип записываемого значения. Само значе­ние записывается в вариантной части записи. Три поля Reserved-l-2-З не используются. Case-константы, определяющие вариантную часть, описаны в модуле System.

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

Какие-либо параметры вариантного типа могут быть определенного ти­па, неназначенные и неизвестные. Неназначенный параметр - это параметр, которому не назначено еще никакого значения. При своем создании пара­метр вариантного типа объявляется как неназначенный и получает значение varEmpty, где varEmpty - константа, определенная в модуле System. Неиз­вестные параметры, как правило, являются ошибочными параметрами.

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

98

ПРОЦЕДУРЫ И ФУНКЦИИ

Обычно программа составляется так, что она состоит из блоков, которые строятся в какой-то степени произвольно. К крупным блокам относятся мо­дули (Unit), библиотеки (DLL). К мелким блокам относятся подпрограммы, которые являются составной частью любых программных единиц. Подпро­грамма выполняет некоторую логически завершенную последовательность действий.

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

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

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

  • локальные переменные, объявленные внутри подпрограммы и доступ­ ные только ей;

  • глобальные переменные, объявленные в блоках, к которым принадле­ жит данная подпрограмма, и доступные как охватывающему подрограмму блоку, так и подпрограмме;

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

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

99

ПРОЦЕДУРА

Заголовок процедуры имеет следующий синтаксис: Procedure <имя>(<список формальных парам.>) ;.Например, пусть заданы 2 мас­сива вещественных чисел, преобразовать их в массивы целых чисел.

Program primer;

Type Arlnt = Array[1..100] of Integer;

ArFloat = Array[1..100] of Double;

var Arlntl, Arlnt2:Arlnt;

ArFloatl, ArFloat2: ArFloat;

procedure Floattolnt (n:byte; const IntArray: Arlnt; var FloatArray: ArFloat);

var i : Byte; Begin

for i:=l to n do IntArray[i]:=round(FloatArray[i]); End; begin

FloatToInt (20, Arlntl, ArFloatl);

FloatToInt {30, Arlnt2, ArFloat2); End.

При разработке подпрограммы следует выделять входные и выходные параметры. В данном случае подпрограмма имеет два входных параметра: п и IntArray - и один выходной - FloatArray. В то же время эти параметры являются формальными параметрами. Фактические параметры - это "на­стоящие" переменные, с помощью которых заменяются формальные пара­метры при выполнении расчетов, т.е. при вызове процедуры (в примере имеются два вызова процедуры между begin и end).

ФУНКЦИЯ

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

Заголовок функции имеет следующий синтаксис: Function <имя> (<список формальных парам.>): <тип возвращаемого результа-та>;.

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

щью выходных формальных параметров, как для процедуры. Например, рас­смотрим вычисление факториала N!=N(N-1).... Напишем функцию для вычисления :

Function Factorial(N:Byte): Cardinal;

Var Fact: Cardinal; i: Byte; begin

If N=0 then Fact:=l else Fact := N;

For i := N-l downto 2 do Fact := Fact * i;

Factorial := Fact; End;.

Здесь введена вспомогательная переменная Fact. Необходимость этого заключается в следующем. Дело в том, что, если справа от знака присваива­ния появляется ИМЯ функции, например, Factorial : = i*Factorial;, TO это означает, что справа при расчете функция вызывает саму себя (рекур­сия). Рекурсия - особая форма математических вычислений, когда функция строится посредством рекурентных соотношений, вызывая саму себя. В дан­ном примере не используется рекурсивная форма расчета факториала, по­этому необходимо написать все операторы без вызова самой функции Fac­torial. Для того чтобы упростить программирование функций и не вводить вспомогательных переменных типа Fact, синтаксически встроена специаль­ная переменная Result, которая имеет тот же тип, что и функция, и исполь­зуется только внутри функций. Перепишем подпрограмму, приведенную выше:

Function Factorial (N : Byte): Cardinal;

Var I : Byte; begin

If N=0 then Result:= 1 else Result:= N;

For i := N-l downto 2 do Result := Result * i; End;.

В данном случае использование справа от знака присваивания перемен­ной Result рекурсии не образует. Присваивание результата расчета имени функции из переменной Result осуществляется автоматически.

Для расчета, например 12!, сначала объявим какую-либо переменную (пусть Q) var q : Cardinal;. Теперь можно записать оператор вычисле­ния 12! Q := Factorial {12) ;.

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

100

101