|
Posledná zmena: 14.11.2002
|
7.11.2002
|
čo sa budeme dnes učiť
- vytvárať vlastné programové
jednotky, štruktúru a vlastnosti unitov
- čo sú to dynamické polia a
ako sa s nimi pracuje
- čo sú to parametre typu otvorené
pole a ako ich môžeme používať
Programové jednotky - unity
- balík "preddefinovaných"
podprogramov, premenných, typov a konštánt
- používajú sa
- ako knižnice podprogramov, ktoré sa dajú
pripojiť k rôznym programom (bez sprístupnenia
zdrojového kódu) - štandardná jednotka
System obsahuje štandardné pascalovské
procedúry a funkcie, napr. AssignFile, Reset,
write, copy, insert, cos, ord, odd, pi, ... ale aj triedu
TObject
- ako programový kód, ktorý popisuje
správanie formulára (najčastejšie má
každý formulár v aplikácii svoju
jednotku - napr. Unit1.pas) - vytvorený je automaticky
prostredím Delphi pri vytváraní
nového formulára (popis komponentov vo
formulári je v súbore s príponou
.DFM)
- často ho môžeme použiť na rozčlenenie rozsiahleho
programu do logicky súvisiacich menších
modulov, resp. na popis nejakej časti hierarchie objektov
- sú v súboroch s príponou .PAS
=> po prekompilovaní je ich kód v súbore
s príponou .DCU (aby sme mohli nejaký
unit použiť v našom programe nepotrebujeme jeho zdrojový
tvar .PAS, ale stačí nám súbor
.DCU)
Definícia unitu:
|
unit menoUnitu; // meno unitu sa musí zhodovať s menom súboru
interface
{ verejné deklarácie a definície konštánt, typov (aj tried),
premenných, procedúr a funkcií (len ich hlavičky), každý, kto
jednotku používa, k nim môže pristupovať ako k svojim vlastným }
uses ...
const ...
type ...
var ...
procedure ...
function ...
implementation
{ definície verejných procedúr a funkcií (ich definície je možné
písať bez ohľadu na poradie a vzájomné odkazy, aj bez formálnych
parametrov, t.j. napísaním len názvu podprogramu), súkromné
deklarácie a definície konštánt, typov, premenných, procedúr
a funkcií }
uses ...
const ...
type ...
var ...
procedure ...
function ...
initialization // tieto časti môžu chýbať
// inicializácia unitu – spustí sa tesne pred spustením
// hlavného programu
finalization
// finalizácia unitu – spustí sa po skončení hlavného programu
end.
|
- Jednotky môžeme použiť v iných programoch,
resp. jednotkách, tak, že do časti deklarácií
uses napíšeme meno jednotky - ak sa jednotka
nenachádza v tom istom adresári ako samotný
projekt, musíme zapísať meno súboru
aj s cestou (meno súboru sa aj tak musí
zhodovať s názvom jednotky), napr.
uses Messages, Vektory in '..\vektory.pas',
PomocnyUnit;
- poradie mien jednotiek v popise uses určuje poradie
ich inicializácií
- príklad demonštruje jednoduchú
programovu jednotku s inicializáciou a finalizáciou
ukážkový unit:
|
unit MojUnit;
interface
procedure zapis(s:string);
implementation
var
t:TextFile;
procedure zapis(s:string);
begin
writeln(t,s);
end;
initialization
AssignFile(t,'d:\test.txt'); Rewrite(t);
finalization
CloseFile(t);
end.
|
Programová jednotka StackUnit - zásobník
- údajová štruktúra zásobník
- stack je pri programovaní veľmi často používaná
a pritom je dosť jednoduchá na to, aby sme na
nej vysvetlili použitie programových jednotiek
- je to údajová štruktúra, ktorá
sa podobá postupnosti prvkov, len spôsob
pridávania a uberania prvkov je špecifický:
- prvky do štruktúry môžeme pridávať
len na koniec - operáciou push
- prvky zo štruktúry môžeme odoberať len
z konca - operácia pop
- pomocná operácia empty slúži
na otestovanie, či je štruktúra prázdna
- zásobníku niekedy hovoríme aj
LIFO (last in - first out) alebo aj nespravodlivý
rad
zásobník zrealizujeme ako triedu:
|
unit StackUnit;
interface
const
maxStack = 100;
type
TPrvok = string;
TStack = class
st: array[1..maxStack] of TPrvok;
vrch: 0..maxstack;
constructor Create;
procedure push(p:TPrvok);
procedure pop(var p:TPrvok);
function top:TPrvok;
function full:boolean;
function empty:boolean;
end;
implementation
uses
Dialogs; // obsahuje ShowMessage
procedure chyba(s:string);
begin
ShowMessage(s); halt; // okno so správou
end;
constructor TStack.Create;
begin
vrch:=0;
end;
procedure TStack.push(p:TPrvok);
begin
if full then chyba('Plný zásobník');
inc(vrch); st[vrch]:=p;
end;
procedure TStack.pop(var p:TPrvok);
begin
if empty then chyba('Prázdny zásobník');
p:=st[vrch]; dec(vrch);
end;
function TStack.top:TPrvok;
begin
if empty then chyba('Prázdny zásobník');
Result:=st[vrch];
end;
function TStack.full:boolean;
begin
Result:=vrch=maxStack;
end;
function TStack.empty:boolean;
begin
Result:=vrch=0;
end;
end.
|
- najlepšie je vytvárať nové unity pomocou
New -> Unit v menu File - vytvorí sa skoro prázdny unit,
napr.
nový unit:
|
unit Unit2;
interface
implementation
end.
|
- tento pomocou Save As... v menu File môžeme
premenovať a tým sa zmení aj hlavička
unitu, napr. na StackUnit
- takto definovaný unit (teda triedu zásobník),
môžeme použiť vo svojom programe
použitie zásobníka:
|
uses StackUnit;
...
procedure TForm1.Button1Click(...);
var
t1,t2:TextFile;
z:TStack;
s:string;
begin
AssignFile(t1,'unit1.pas'); Reset(t1);
AssignFile(t2,'x.txt'); Rewrite(t2);
z:=TStack.Create;
while not eof(t1) do begin
readln(t1,s); z.push(s);
end;
while not z.empty do begin
z.pop(s); writeln(t2,s);
end;
z.Free;
CloseFile(t1); CloseFile(t2);
end;
|
- ešte si všimnite, že na záver programu
sme použili z.Free - podobne ako pri bitmapách
je dobre na záver nezabudnúť objekt
zrušiť t.j. uvolniť z pamäti počítača
- pozrime sa, ako vyzerá "hlavný
program", t.j. Delphi projekt aplikácie
Project1.dpr:
hlavný program:
|
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
StackUnit in 'StackUnit.pas';
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
|
- tento súbor je tiež pascalovský zdrojový
kód, ale na rozdiel od programovej jednotky obsahuje
len deklarácie a "hlavný program",
t.j. to, čo sa spustí po štarte programu (po inicializácii
unitov) -- zrejme Application je nejaká špeciálna
inštancia (zrejme je deklarovaná v unite Forms), o ktorú sa stará Delphi a tento
hlavný program len zavolá nejaké
3 metódy tejto inštancie.
Dynamické polia
- deklarujeme ich
var a:array of typ_prvku;
- znamená to, že premenná a tohoto typu
zatiaľ nemá určenú veľkosť, t.j. počet
prvkov
- veľkosť poľa musíme zadefinovať pomocou
- procedúry SetLength(a,počet_prvkov); vyhradí
pamäť
- a:=nil; znamená, že pole je prázdne
=> nulový počet prvkov (niečo podobné
ako pri objektoch) - toto je to isté ako SetLength(a,0);
- a:=copy(iné_pole,od_prvku,počet); rovnako
ako pri znakových reťazcoch
- štandardná funkcia Length vracia momentálnu
veľkosť poľa (pre nil je to 0)
- takto definované pole má indexy vždy
od 0 po Length(pole)-1, t.j. Low(a) je 0 a High(a) je
Length(a)-1
- ak už pole obsahuje nejaké prvky a meníme
jeho veľkosť pomocou SetLength, buď nejaké prvky
stratíme, alebo pri zväčšovaní dĺžky
pribudnú na koniec nové prvky - zatiaľ
ešte s nedefinovanou hodnotou
zásobník zrealizujeme pomocou dynamického
poľa:
|
unit StackUnit;
interface
type
TPrvok = string;
TStack = class
st:array of TPrvok;
constructor Create;
procedure push(const p:TPrvok);
procedure pop(var p:TPrvok);
function top:TPrvok;
function empty:boolean;
end;
implementation
uses
Dialogs;
procedure chyba(const s:string);
begin
ShowMessage(s); halt;
end;
constructor TStack.Create;
begin
st:=nil;
end;
procedure TStack.push(const p:TPrvok);
begin
SetLength(st,Length(st)+1); st[High(st)]:=p;
end;
procedure TStack.pop(var p:TPrvok);
begin
if empty then chyba('Prázdny zásobník');
p:=st[High(st)]; SetLength(st,Length(st)-1);
end;
function TStack.top:TPrvok;
begin
if empty then chyba('Prázdny zásobník');
Result:=st[High(st)];
end;
function TStack.empty:boolean;
begin
Result:=Length(st)=0; // alebo Result:=st=nil;
end;
end.
|
- Nakoľko môže byť operácia "nafukovania"
dynamického poľa pomerne "drahá",
t.j. časovo náročná, môžeme pole
zväčšovať po väčších úsekoch,
napr. 10:
dynamické pole sa "nafukuje"
po väčších krokoch:
|
unit StackUnit;
interface
type
TPrvok = string;
TStack = class
st:array of TPrvok;
vrch:integer;
constructor Create;
procedure push(const p:TPrvok);
procedure pop(var p:TPrvok);
function top:TPrvok;
function empty:boolean;
end;
implementation
uses
Dialogs;
procedure chyba(const s:string);
begin
ShowMessage(s); halt;
end;
constructor TStack.Create;
begin
st:=nil; vrch:=-1;
end;
procedure TStack.push(const p:TPrvok);
begin
inc(vrch);
if vrch>High(st) then SetLength(st,Length(st)+10);
st[vrch]:=p;
end;
procedure TStack.pop(var p:TPrvok);
begin
if empty then chyba('Prázdny zásobník');
p:=st[vrch]; dec(vrch);
if vrch<High(st)-20 then SetLength(st,Length(st)-10);
end;
function TStack.top:TPrvok;
begin
if empty then chyba('Prázdny zásobník');
Result:=st[vrch];
end;
function TStack.empty:boolean;
begin
Result:=vrch<0;
end;
end.
|
- príklad, v ktorom použijeme zásobník
spolu s korytnačkou (predpokladáme, že prvky
zásobníka sú čísla, napr.
type TPrvok=real;):
:
|
procedure TForm1.Button1Click(Sender: TObject);
var
z:TStack;
k:TKor;
i:integer;
r:real; // TPrvok
begin
k:=TKor.Create;
z:=TStack.Create;
for i:=1 to 20 do begin
k.Dopredu(i*10); k.Vpravo(90); z.push(i*10);
end;
cakaj(1000);
k.fp:=clRed;
while not z.empty do begin
z.pop(r); k.vlavo(90); k.dopredu(-r);
end;
z.Free;
end;
|
Parameter otvorené pole
- Potrebujeme procedúru, ktorá dostane
"postupnosť" čísel a na jej základe
nakreslí krivku tak, že postupne z nej bude brať
dĺžky pre príkaz dopredu a zakaždým sa
otočí o 90 stupňov vpravo. Nakoľko nepoznáme
dĺžku vstupnej postupnosti (t.j. poľa) použijeme tzv.
parameter otvorené pole, t.j. skutočným
parametrom môže byť ľubovoľné jednorozmerné
pole, ktoré sa zhoduje s typom prvkov. Zapisujeme,
napr.
procedure kresli(const d:array of real);
- takýto formálny parameter nie je dynamické
pole!!!
- Vo vnútri procedúry pristupujeme k
prvkom poľa s indexmi od Low(d) až po High(d), pričom
Low(d) je vždy 0 a High(d) je vždy Length(d)-1. Nesmieme
použiť SetLength ani copy.
príklad:
|
var
k:TKor;
procedure kresli(d:array of real);
var
i:integer;
begin
for i:=low(d) to high(d) do begin // low(d) je vždy 0
k.Dopredu(d[i]);
k.Vpravo(90);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
const
dd:array[5..10] of real = (50,50,100,100,50,50);
begin
k:=TKor.Create;
kresli(dd);
end;
|
- Delphi umožňujú poslať ako skutočný
parameter typu otvorené pole aj konštantu otvorené
pole, t.j. "postupnosť" hodnôt rovnakého
typu ako prvky otvoreného poľa uzavretú
v hranatých zátvorkách, napr.
použitie konštanty otvorené pole:
|
procedure TForm1.Button1Click(Sender: TObject);
begin
k:=TKor.Create;
kresli([50,50,100,100,50,50]);
end;
|
- už poznáme procedúru Polygon, ktorá
funguje v Canvase grafickej plochy, má parameter
otvorené pole typu TPoint a štandardná
funkcia Point vyrába záznam typu TPoint,
t.j. niečo ako
štandardná procedúra Polygon:
|
type
TPoint = record x,y:integer end;
procedure TCanvas.Polygon(const Points: array of TPoint);
...
function Point(AX, AY: integer): TPoint;
begin
Result.x:=AX; Result.y:=AY;
end;
|
- môžeme jednoducho volať takúto procedúru
konštantou otvoreného poľa, napr.
volanie procedúry Polygon:
|
g.Polygon([Point(100,100),Point(200,150),Point(50,200)]);
|
|
© 2002 AB, KVI blaho@fmph.uniba.sk
|