- •Windows -приложение
- •Среда программирования
- •Встроенный отладчик
- •Использование графики
- •Графические данные и палитра
- •Сохранение проекта
- •Структура приложения
- •Структура модуля
- •Простые типы
- •Символьные типы
- •Логические типы
- •Тип перечень
- •Составной оператор
- •Оператор if
- •Оператор цикла for
- •Оператор цикла while
- •Оператор цикла repeat
- •Пример приложения 6
- •Пример приложения 7
- •Статические массивы
- •Динамические массивы
- •Оператор with
- •Идентичность типов
- •Совместимость типов
- •Преобразование типов
- •Операторы обработки исключительных ситуаций
- •Рекурсия
- •Процедура exit
- •Директивы подпрограммы
- •Класс как объектный тип
- •Наследование
- •Операции is и as
- •Типы ссылки на класс
- •Типизированные файлы
- •Файлы без типа
- •Пример приложения 17
- •Компонент tmainmenii
- •Двунаправленные списки
- •Потоки данных
- •Пример приложения 22
- •Интерфейс drag and drop
- •Пример приложения 24
- •С файлами
- •Пример приложения 26
- •Программные потоки
- •Приоритеты потоков
- •Класс tthread
- •Проблемы синхронизации потоков
Идентичность типов
Если при объявлении использовать один и тот же идентификатор или несколько переменных объявляются с помощью одного описания, то такие типы идентичны. Например
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 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 это означает, что справа при расчете функция вызывает саму себя (рекурсия). Рекурсия - особая форма математических вычислений, когда функция строится посредством рекурентных соотношений, вызывая саму себя. В данном примере не используется рекурсивная форма расчета факториала, поэтому необходимо написать все операторы без вызова самой функции Factorial. Для того чтобы упростить программирование функций и не вводить вспомогательных переменных типа 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