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

Двунаправленные списки

Структура двунаправленного связанного списка приводится на рис. 42. Данная структура соответствует следующему объявлению:

Type PStroka = AStroka; Stroka = record

Info:string;

Pred,Sled: PStroka; End;.

Рис. 42

Для построения связей между отдельными элементами списка исполь­зуются два указателя: Pred - связь с предыдущим элементом и sled - связь с последующим элементом. Соответственно имеет место ограничение связей элементов списка и слева, и справа с помощью пустого указателя nil. По­строим список из трех элементов, как в Предыдущем случае, и при условиях предыдущей задачи.

New(Spisok);

SpisokA.Info:=S1;

SpisokA.Pred:=nil;

Hew (P) ;

PA.Info:=S2;

PA.Pred:=Spisok;

SpisokA.Sled:=P;

New{P) ;

PA.Info:=S3;

PA .Pred:=SpisokA.Sled;

SpisokA.SledA.Sled:=P;

PA.sled:=nil;

P:=nil;.

{Начало списка}

{Запись строки SI в список}

{Ограничение связей списка слева}

{Новый элемент списка}

{Запись строки S2 в элемент Р}

{Связь с предыдущим элементом}

{Подключение элемента два в список}

{Третий элемент списка}

{Запись строки S3 в элемент Р}

{Связь с предыдущим элементом}

{Подключение элемента 3 в список}

{Конец списка}

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

154

155

Тогда получим структуру дерева, представленную на рис. 43. Структуру би­нарного дерева синтаксически можно объявить следующим образом:

СТЕКИ, ОЧЕРЕДИ

Предполагается, что элементы в списках могут добавляться и извлекать­ся в произвольном порядке. Существуют списки с заранее заданными проце­дурами добавления и извлечения элементов. Это стеки и очереди. Очередью называется однонаправленный или двунаправленный связанный список с процедурой работы FIFO - First In, First Out (первым пришел, первым ушел). В списках типа очередь элемент добавляется в конец списка, а извле­кается из начала списка. Стек - связанный список с процедурой работы LIFO - Last In, First Out (последним пришел, первым ушел). Элементы стека добавляются в конец списка и извлекаются из конца списка.

Идентифицируется очередь с помощью двух указателей: запоминается начало очереди и конец списка. Стек можно идентифицировать одним указа­телем, помечая дно стека с помощью nil.

БИНАРНЫЕ ДЕРЕВЬЯ

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

Бинарное дерево, по определению, - это иерархическая структура, схе­матично представляемая, например, так, как показано на рис. 43.

Рис.43

Бинарное дерево имеет узлы, в которые может входить одна ветвь, а вы­ходить не более двух (бинарное). Верхняя вершина не имеет входа и называ­ется корнем. Построить бинарное дерево можно следующим образом. Пусть имеется набор числовых оценок для некоторого ключа, например, такой: 35, 86, 49, 27, 31, 56, 62, 44, 29, 26, 33, 88. Примем, что вправо по ходу дерева, начиная от корня, значение ключа увеличивается, а влево - уменьшается.

Type

PDerevo=ADerevo Derevo=record

Key:word;

Info: string;

Left,Right: Pderevo; End; .

В заключение рассмотрим такую задачу. Пусть требуется построить оче­редь для данных, содержащихся в текстовом файле a. txt. Можно записать:

Program Prim;

Type PStroka = ^Stroka.;

Stroka = record;

Info:string;

Sled: PStroka;

End;

Var T:TextFile;

P,Pbegin,Pend:PStroka;

S: string;

Begin

New(Pbegin);

Pend:=Pbegin;

P:=Pbegin;

AssignFile(T,'A.txt');

Reset(T);

While not eof(T) do begin

Readln(T,S);

Pend^.Sled:=P;

Pend:=P;

New(P);

End;

Pend^.sled:=nil;

Dispose (P);

CloseFile(T);

End.

Здесь указатель pbegin является идентификатором начала очереди, по­этому ему еще до начала цикла выделяется память (New (Pbegin) ;). Указа­тель pend необходим для запоминания последнего элемента очереди. Вспо­могательный указатель р позволяет обеспечить циклические вычисления. Цикл построен так, что указатель Pend постоянно продвигается по списку, обеспечивая добавление очередного элемента в конец очереди.

156

157

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

Среда Delphi содержит встроенный класс TList, с помощью которого можно создавать однонаправленные списки. Этот класс предназначен для организации списка указателей на размещенные в адресном пространстве какие-либо структуры данных и обеспечивает эффективную работу с эле­ментами списка. Доступ к отдельному элементу списка осуществляется по­средством индексированного свойства ItemsfIndex]. Нумерация элементов начинается с нуля. Элементы списка можно добавлять в его конец с помо­щью метода Add или в середину, используя метод Insert. Можно реализо­вать сортировку элементов с помощью метода Sort. Однако, так как заранее состав структуры, на которую указывают указатели списка, неизвестен, не­обходимо методу Sort передавать адрес разрабатываемой программистом процедуры попарного сравнения каких-либо полей, входящих, в состав структуры отдельного элемента списка. Список создается посредством стан­дартного конструктора Create. Соответственно необходимо обеспечить его удаление в приложении с помощью деструктора Free.



Интерфейс и возможности приложения можно увидеть, изучая вариант решения данного примера (рис. 44). Окружности рисуют, используя обра­ботчик ImagelMouseDown.

В примере 19 реализуется запись в список TList некоторой структуры данных, представляющей собой следующий тип (Туре), используемый для рисования окружностей:

pMyList = ^AList; AList = record

numP: Integer;

xP,yP:integer;

r:integer;

Col: TColor; end;.

Здесь numP - номер окружности; xp, yP - координаты центра окружности; r-радиус; Col - цвет.

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

unit Unit1ist; interface

uses Windows, Messages, SysUtils, Variants, Classes,Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Buttons,UnitSearch; type

TForm1 = class (TForm) Edit1: TEdit; Label1: TLabel; Label2: TLabel; Imagel: Tlmage; Button1: TButton; Bevel1: TBevel; Button2: TButton;

Button3: TButton;

BitBtn1: TBitBtn;

Bevel2: TBevel;

Button4:TButton;

ProcedureFormCreate(Sender:TObject)

procedureFormClose(Sender:TObject;var Action: TCloseAction);

procedure Edit1KeyPress{Sender: TObject; var Key: Char);

procedure ImagelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure Button3Click (Sender: TObject) ;

procedure Button4Click(Sender: TObject);

public

158

159

MyList: TList;

end;

PMyList = ^AList;

AList = record

numP: Integer; xP,yP:integer; r:integer; Col: TColor; end; var Form1: TForm1;

ARecord: PMyList; kZap:integer=0; implementation uses UnitColor; {$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject); begin

MyList := TList.Create;

Imagel.Canvas.Pen.Width := 2;

end;

procedure TForm1.PormClose(Sender:TObject;

var Action: TCloseAction);

begin

Dispose(ARecord);

MyList.Free; end;

procedure TForm1.Edit1KeyPress(Sender: TObject;

var Key: Char); begin

if not {key in ['O'..'9',#8]) thenkey:=#0; end;

procedure TForm1.ImagelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin

New{ARecord);

ARecord^.xP := x;

ARecord^.yP := y;

if length{Edit1.Text)=0 then Edit1.Text:='0';

ARecord^.R := StrToInt(Edit1.Text);

inc(kZap);

ARecord^.numP:=kZap;

Form2.ShowModal;

ARecordA.Col:=Form2.NewColor;

MyList.Add (ARecord) ;

with Imagel.Canvas, ARecord* do begin

Pen.Color := ARecord^.Col;

Ellipse (xP - R, yP - R, xP + R, ур + R);

TextOut(xP-Font.Size div 2,

160

yP+Font.Height div 2, IntToStr(numP));

end;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

Imagel.Canvas.Brush.Color:=clWhite; Imagel.Canvas.Rectangle(0,0, Imagel.width,Imagel.Height);

end;

function CompareX(Iteml, Item2: Pointer): Integer;

begin

Result :=PMyList(Iteml)^.xP-PMyList(Item2)^.xP;

end;

procedure TForm1.Button2Click(Sender: TObject);

var i:integer; begin

MyList.Sort(OCompareX);

for i := 0 to (MyList.Count - 1) do begin ARecord:=MyList.Items[i]; ARecord^.numP:=i+l; end; end;

procedure TForm1.Button3Click(Sender: TObject);

var i:integer; begin

for i. := 0 to (MyList .Count - 1) do begin ARecord := MyList.Items[i]; with imagel.Canvas, ARecord^ do begin Pen.Color := Col;

Ellipse (xP - R, yP - R, xP + R, yP + R); TextOut(xP-Font.Size div 2,

yP+Font.Height div 2, IntToStr(numP));

end;

end;

end;

procedure TForm1.Button4Click(Sender: TObject); begin

Form3.Show; end; end.

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

unit UnitColor;

161

interface

uses Windows, Messages, SysUtils, Variants, Classes,Graphics,

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

TForm2 = class(TForm) ColorBox1: TColorBox; Label1: TLabel; procedure ColorBox1Change(Sender: TObject);

public

NewColor:TColor;

end;

var Form2: TForm2; implementation

($R *.dfm}

procedure TForm2.ColorBox1Change(Sender: TObject); begin

NewColor:=ColorBox1.Selected;

close; end; end.

Интерфейс второй формы представлен на рис. 45. После выбора цвета осуществляется автоматический возврат в основную форму.

Рис. 45

Программный код третьего модуля приводится ниже.

unit UnitSearch;

interface

uses Windows, Messages, SysUtils, Variants, Classes,Graphics,

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

TForm3 = class(TForm) Label1: TLabel; Edit1: TEdit;

162

BitBtn1: TBitBtn; Button1: TButton; Bevell: TBevel; Bevel2: TBevel;

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

var Form3: TForm3; implementation uses Unit1ist, UnitColor;

{$R *.dfm} procedure TForm3.Edit1KeyPress(Sender: TObject;

var Key: Char);

begin

if length(Edit1.Text)*0 then Edit1.Text:='0'; if not (key in ['0'..'9',#8]) then key:=#0;

end;

procedure TForm3.Button1Click(Sender: TObject);

var k,i:integer; begin

if length(Edit1.Text)=O then Edit1.Text:='0'; k:=StrToInt{Edit1.Text);

if (k = 0) or (k > kZap) then begin

ShowMessage(

'Неверно задан номер точки');

exit; end;

for i := 0 to (Form1.MyList.Count - 1) do begin

ARecord := Form1.MyList.Items[i];

if ARecord^.numP=k then begin

with Canvas, ARecord^ do begin Pen.Color := Col;

Ellipse (xP - R, yP - R, xP + R, yP + R); TextOut(xP~Font.Size div 2,

yP+Font.Height div 2, IntToStr(numP)); end; break;

end;

end;

end; end.

В данном модуле предусмотрено рисование окружности не на элементе bnage, а непосредственно на канве формы, которая представлена на рис. 46. Модуль UnitSearch позволяет организовать поиск заданного элемента в сформированном связанном списке.

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

163

и вспомогательные формы. Вызов других форм организуется с помощью кнопок или меню посредством метода Show (активная форма) или showHo-dal (неактивная форма).

Рис. 46 1

В программе выше используются следующие вызовы других форм: Form2.ShowModal, Form3.Show.

ПРОЦЕДУРНЫЙ ТИП

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

Туре Р1:Procedure; Р2;Procedure{var X,Y:real);

РЗ:Function(X:integer):double; Var Q2:P2; Q3:P3;.

Далее в программе может быть объявлено несколько функций с одним параметром, как указано в РЗ, например: Function Sum(X: integer) : double;, Funcnion Mult (X:integer) :double;. Далее можно записать следующие операторы: Q3:= sum; Y: = Q3(8); Q3:= Mult; Y:= Q3 (Z) ; Q2:= nil;.

В качестве значения переменной процедурного типа не могут использо­ваться стандартные подпрограммы из модуля System.

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

ПРОГРАММНЫЕ ЕДИНИЦЫ DLL

DLL означает Dynamic Link Library {динамически компонуемая биб­лиотека). DLL - это библиотеки, подключаемые к другим программным единицам во время их выполнения. Удобство их состоит в том, что ресурсы библиотек могут использоваться одновременно несколькими программами. Применение DLL не зависит от языка, на котором она написана. DLL со­ставляют основу архитектуры Windows. Модель программирования на осно­ве DLL позволяет добиться модульности всех элементов системы, а модуль­ность операционной системы - предоставить программисту более динамич­ный и открытый интерфейс.

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

Заголовок DLL состоит из зарезервированного слова Library и имени библиотеки. Имя библиотеки совпадает с именем файла, в который запи­сывается ее программный код. Раздел экспортирования начинается с заре­зервированного слова Exports. Секция инициализации начинается с ключе­вого слова begin, а заканчивается end с точкой. Назначение ее такое же, как у аналогичной секции модуля Unit. Эта секция может отсутствовать. Осталь­ные разделы могут присутствовать в любом количестве, как и в других про­граммных единицах.

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

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

Экспортируемые подпрограммы должны иметь внутреннее (в DLL) имя и могут иметь необязательное внешнее имя, которое используется как аль­тернатива при импортировании, например Exports Subroutinel name AddC, Subroutine2;.

164

165

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

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

Так как необходимо ввести новый тип, декларация которого требуется в двух программных единицах (основной модуль, библиотека), создадим до­полнительный модуль DeclComplex, содержащий только одно объявление типа. Данный пример дополним еще одной формой, в которой представим сведения о программе (рис. 47). Основная форма для этого примера пред­ставлена на рис. 48.

Рис. 48

При разработке данного приложения вначале необходимо построить библиотеку. Затем создать проект с формой рис. 48. Далее последовательно добавить в этот проект модуль с формой, представленной на рис. 47, и мо-

дуль с объявлением типа Complex. Ниже приводятся тексты кодов исполь­зуемых программных единиц.

unit prim20; {Модуль с основной формой.}

interface

uses Windows, Messages, SysUtils, Classes, Graphics,

Controls, Forms, Dialogs, StdCtrls, Buttons,

ExtCtrls, Menus, DeclCoroplex, About;

type

Рис. 47

TMainForm = class(TForm) Bevel1: TBevel; Bevel2: TBevel; Edit1: TEdit; Label1: TLabel; Edit2: TEdit; Label2: TLabel; Label4: TLabel; Edit5: TEdit;

Edit6: TEdit;

Edit3: TEdit;

Edit4: TEdit; Label3: TLabel; Label5: TLabel; Label6: TLabel; Label7: TLabel; Label8: TLabel; Label9: TLabel; Label1O: TLabel; Label11: TLabel; MainMenu1: TMainMenu; Calc: TMenuItem; About: TMenuItem; ExitPoint: TMenuItem; Input: TMenuItem;

procedure Edit1KeyPress(Sender: TObject; var Key: Char); procedure CalcClick(Sender: TObject);

procedure ExitPointClick(Sender: TObject);

procedure InputClick(Sender: TObject);

procedure AboutClick(Sender: TObject);

end;

var MainForm: TMainForm;

Function AddC(X,Y:Complex) :Complx; external 'MyDLL.dll’;

implementation

{$R *.DFM}

procedure TMainForm.Edit1KeyPress(Sender: TObject;

var Key: Char);

begin

if not (key in ['0’..'9',’-',#8]) thenkey:=#0;

167

end;

procedure TMainForm.CalcClick(Sender: TObject);

var X,Y,Z:Complex; begin

X.Re:=StrToInt(Edit1.Text);

X.lm:=StrToInt(Edit2.Text);

Y.Re:=StrToInt(Edit3.Text);

Y.im:=StrToInt(Edit4.Text);

Z:=AddC(X,Y);

Edits.Text := FormatFloat('0.00',Z.Re);

Edit6.Text := FormatFloat('0.00',Z.Im); end;

procedure TMainForm.ExitPointClick(Sender: TObject); begin

Close; end;

procedure TMainForm.InputClick(Sender: TObject); begin

Edit1.SetFocus; end;

procedure TMainForm.AboutClick(Sender: TObject); begin

AboutForm.ShowModal;end; end.

{Модуль с дополнительной формой.}

unit About; interface uses

Windows, Classes, Graphics, Forms, Controls, StdCtrls, Buttons, ExtCtrls; type

TAboutForm = class(TForm) Panel1: TPanel; OKButton: TBitBtn; Programlcon: Tlmage; ProductName: TLabel; Department: TLabel; Copyright: TLabel; Comments: TLabel; end;

var AboutPorm: TAboutForm; implementation {$R *.DFM} end.

unit DeclComplex; {Модуль с объявлением типа Complex.}

interface

Type

Complex=record

168

Re,im:Real;

end;

implementation end.

Library MyDLL; {Программа DLL.)

uses SysUtils, Classes, DeclComplex; function AddC(X,Y:complex):complex; begin

with result do begin Re:=x.Re+Y.Re;

end; end;

Exports AddC; begin end.

Импортирование подпрограмм другими программными единицами воз­можно в статическом (неявном) или динамическом (явном) режимах. Если используется статическое импортирование подпрограммы, то задается заго­ловок такой подпрограммы с командой External. Если DLL является Win­dows-библиотекой или, например, написанной на C++, то необходимо ука­зать соглашения о вызовах. Например, Procedure Procl (var x: Vari­ant) ; Cdecl; External 'CDLL.dll';. В этом примере из библиотеки cdll . dll, написанной на C++, указано соглашение этого языка о передаче данных Cdecl.

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

При динамическом импортировании библиотека грузится непосредст­венно внутри той программной единицы, которой она необходима. После того как надобность в библиотеке исчезнет, ее нужно выгрузить. Для загруз­ки используется стандартная функция LoadLibrary (API-функция) с единст­венным параметром типа PChar, задающим имя библиотеки, например var Handle:HModule; . . . Handle: =LoadLibrary ('MyDll.dll') ;. Для вы­грузки DLL используется подпрограмма FreeLibrary (Handle) ;.

Для работы с библиотекой необходимо получить адрес требуемой функ­ции (или процедуры). При этом используется API-функция GetProcAd-ress(Handle: HModule, lpProcName: LPCSTR): FARPROC, которая ищет подпрограмму, указанную именем lpProcName. В качестве результата возвращается указатель на функцию (или процедуру) или nil. Полученный указатель должен быть далее приведен к нужному типу. Например, возмо­жен такой вариант:

Onit uCallDll; Interface

169

type TAddC = Function(X,Y:Complex):Complex; TForm1=class(TForm)

…………………

Private {Добавим в описание формы}

DLLHandle: HModule; ExternalMyFunctionPointer:TFarProc; ExternalMyFunction:TAddC;

……………………………………………

Implementation

……………………………………

ProcedureTForm1.Button1Click(Sender:TObject);

………………………………

DLLHandle:=LoadLibrary('MyDll.dll'); ExternalMyFunctionPointer:= GetProcAdress(DLLHandle,

PChar('AddC'));

ExternalMyFunction:=TAddC(ExternalMyFunctionPointer)

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

ТЕХНОЛОГИИ ПРОГРАММИРОВАНИЯ

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