15.10.2002
|
Pole znakových reťazcov
- údajová štruktúra pole
môže mať prvky ľubovoľného typu, teda
aj znakový reťazec
- s takýmito premennými, ktoré
sú prvkami poľa, sa pracuje rovnako ako s
jednoduchými premennými typu reťazec
program ilustruje použitie poľa
znakových reťazcov:
|
var
t:TextFile;
p:array [1..1000] of string;
i,j,n:integer;
begin
AssignFile(t,'unit1.pas'); Reset(t);
n:=0;
while not eof(t) and (n<high(p)) do begin
inc(n); readln(t,p[n]);
end;
CloseFile(t);
for i:=1 to n do
for j:=1 to length(p[i]) do
if (j=1) or (p[i][j-1]=' ') then p[i][j]:=upcase(p[i][j]);
Memo1.Clear;
for i:=1 to n do
Memo1.Lines.Add(p[i]);
end;
|
- zápis p[i][j]
označuje j-ty znak i-teho reťazca - môžeme
to zapísať skrátene: p[i,j]
časť programu filtruje medzery
na začiatku a konci každého reťazca:
|
...
for i:=1 to n do begin
j:=1; while (j<=Length(p[i])) and (p[i,j]=' ') do inc(j);
delete(p[i],1,j-1);
j:=Length(p[i]); while (j>=1) and (p[i,j]=' ') do dec(j);
SetLength(p[i],j);
end;
...
|
- zrejme toto isté by sa dalo jednoduchšie
zrealizovať pomocou štandardnej funkcie Trim
Textová plocha
zmeníme každý riadok textovej plochy:
|
procedure TForm1.Button1Click(Sender: TObject);
var
i:integer;
begin
for i:=0 to Memo1.Lines.Count-1 do
Memo1.Lines.Strings[i]:='<'+Memo1.Lines.Strings[i]+'>';
end;
|
- logická stavová premenná Memo1.ReadOnly
slúži na to, aby sme používateľovi mohli
povoliť/zakázať zapisovať do textovej plochy,
napr. Memo1.ReadOnly:=false; zakáže používateľovi
modifikovať plochu pomocou klávesnice a myši
(príkazmi z programu je to stále dovolené)
Viacrozmerné polia
Zovšeobecníme polia: prvkami poľa môže
byť opäť pole -- ak je prvkom jednorozmerné
pole, tak výsledný typ je dvojrozmerné
pole. Napr. a[4][2]
označuje druhý prvok v štvrtom poli - väčšinou
si dvojrozmerné pole predstavujeme ako tabuľku,
v ktorej každý prvok leží v nejakom riadku
a nejakom stĺpci, napr. a[4][2] môže
označovať prvok v 4-tom riadku a v 2-om stĺpci - skrátene
to môžeme zapísať a[4,2]
- Príklad: Je dané dvojrozmerné
pole znakov, ktoré je už zaplnené. Napíšte
program, ktorý presunie prvý riadok do
druhého, druhý do tretieho, atď., až posledný
do prvého (motivácia: rolovanie obrázka
ako vo svetelných novinách).
Najprv nie pekné riešenie:
|
var
a:array[1..10] of array[1..5] of char;
r:array[1..5] of char;
i,j:integer;
begin
... // zaplnenie poľa
for i:=1 to 5 do r[i]:=a[10,i];
for i:=9 downto 1 do
for j:=1 to 5 do
a[i+1,j]:=a[i,j];
for i:=1 to 5 do a[1,i]:=r[i];
...
|
a teraz krajšie
|
type
riadok=array[1..5] of char;
pole=array[1..10] of riadok;
var
a:pole;
r:riadok;
i:integer;
begin
...
r:=a[10];
for i:=9 downto 1 do a[i+1]:=a[i];
a[1]:=r;
...
|
- využili sme to, že typ každého riadka
poľa a je rovnaký ako premenná
r
- nasledujúci príklad ukazuje spôsob
prezerania prvkov dvojrozmerného poľa (zisťujeme,
či je matica NxN symetrická)
nie najlepší spôsob riešenia:
|
const
N=10;
type
pole=array[1..N,1..N] of integer;
var
a:pole;
i,j:integer;
ok:boolean;
begin
// ... inicializácia poľa
ok:=true;
for i:=2 to N do
for j:=1 to i-1 do
if a[i,j]<>a[j,i] then begin
ok:=false;
break;
end;
von:
if ok then // ... je symetrická
...
end;
|
- toto riešenie sa snaží použiť príkaz
break, ktorý spôsobí
vyskočenie z najvnútornejšieho cyklu - toto
je nedostatočné, lebo aj tak sa budú
kontrolovať ešte všetky nasledujúce riadky
poľa
- ďalšie riešenie je zlé - používa príkaz
goto - tento príkaz je pre nás
absolútne zakázaný
veľmi zlý spôsob riešenia:
|
...
ok:=true;
for i:=2 to N do
for j:=1 to i-1 do
if a[i,j]<>a[j,i] then begin
ok:=false;
goto von; // !!! je to absolútne zakázané !!!
end;
...
|
správne riešenie:
|
...
ok:=true; i:=2;
while ok and (i<=N) do begin
j:=1;
while ok and (j<i) do begin
ok:=a[i,j]=a[j,i];
inc(j)
end;
inc(i);
end;
...
|
správne riešenie s využitím exit
vo funkcii:
|
function test(const a:pole):boolean;
var
i,j:integer;
begin
Result:=false;
for i:=2 to N do
for j:=1 to i-1 do
if a[i,j]<>a[j,i] then exit;
Result:=true;
end;
|
- príkaz exit znamená okamžité
ukončenie podprogramu - ako keby sa skočilo na posledné
end podprogramu
Kopírujeme a modifikujeme obrázky
Aby sa nám lepšie natrénovala práca
s dvojrozmerným poľom, vytvoríme si v
Delphi nový projekt, v ktorom budú dve
"grafické" plochy. Do prvej načítame
z nejakého súboru bitmapu a túto
potom prekopírujeme do druhej plochy. Pri kopírovaní
ju môžeme rôzne meniť, prípadne meniť
spôsoby kopírovania. S obrázkami
budeme pracovať ako s dvojrozmerným poľom, pričom
prvý index poľa bude vyjadrovať x-ovú
súradnicu farebnej bodky (pixel). Bodky číslujeme
od 0 do šírka-1.
- Najprv si pripravme nový projekt -- do formulára
vložíme dve grafické plochy Image, upravíme
im rozmery, aby boli rovnako veľké 250x250
- do formulára vložíme niekoľko tlačidiel
-- postupne im budeme priraďovať rôzne funkcie
(dvojkliknutím na príslušné tlačidlo)
-- formulár teraz vyzerá nejako takto:

- projekt uložíme do nejakého adresára
na disku a tiež sem prekopírujeme niekoľko obrázkov
(súborov s príponou .BMP) -- môžete
si stiahnuť tieto súbory obrazky.zip
- dvojklikneme na Button1 a priradíme mu akciu
načítanie bitmapy napr. tiger.bmp do prvej grafickej
plochy Image1:
do grafickej plochy sa načíta
obrázok zo súboru:
|
procedure TForm1.Button1Click(Sender: TObject);
begin
Image1.Picture.LoadFromFile('tiger.bmp');
end;
|
- podobne môžeme tlačidlu Button2 priradiť nejakú
inú bitmapu
- tlačidlo Button4 bude slúžiť na kopírovanie
obrázka z Image1 do plochy Image2
- stavová premenná Pixels
nám umožňuje pristupovať k jednotlivým
pixelom (farebným bodkám) obrázka
- funguje to na rovnakom princípe ako Strings
v textovej ploche Memo - teda Pixels
je ako dvojrozmerné pole prvkov typu TColor
(farba)
postupné kopírovanie
po riadkoch:
|
procedure TForm1.Button4Click(Sender: TObject);
var
g1,g2:TCanvas;
x,y:integer;
begin
g1:=Image1.Canvas; g2:=Image2.Canvas;
for y:=0 to Image1.Height-1 do begin
for x:=0 to Image1.Width-1 do
g2.Pixels[x,y]:=g1.Pixels[x,y];
Image2.Repaint;
end;
end;
|
- Image2.Repaint prekreslí druhú grafickú
plochu po prekopírovaní každého
riadka -- inak by sa zobrazila až záverečná
zmena
- ďalší variant sa hrá s farbami cez
ich položky RGB:
práca so zložkami farby
bodov:
|
procedure TForm1.Button5Click(Sender: TObject);
var
g1,g2:TCanvas;
x,y:integer;
r,g,b:byte;
c:TColor;
begin
g1:=Image1.Canvas; g2:=Image2.Canvas;
for y:=0 to Image1.Height-1 do begin
for x:=0 to Image1.Width-1 do begin
c:=g1.Pixels[x,y];
r:=GetRValue(c); g:=GetGValue(c) div 2; b:=GetBValue(c);
g2.Pixels[x,y]:=RGB(r,g,b);
end;
Image2.Repaint;
end;
end;
|
- funkcia GetRValue vráti červenú zložku
farby, podobne GetGValue a GetBValue vrátia
zelenú a modrú zložku
Práca s myšou
- v Delphi sa s myšou pracuje pomocou udalostí,
ktoré automaticky vznikajú pri každom
pohybe myši na obrazovke
- pri pohybe myši túto udalosť dostáva
ten komponent formulára, nad ktorým
sa myš nachádza
- procedúra, ktorá sa vyvolá
ako reakcia na udalosť (event driver) pohyb
myši, dostáva ako parametre aj súradnice
myši (X,Y) - tieto sú vždy relatívne
vzhľadom na ľavý horný roh komponentu
- napíšeme procedúru,
ktorá sa zavolá vždy, keď pohneme kurzorom
myši nad plochou Image2 - táto procedúra
prekopíruje len jednu bodku z Image1 a to presne
z pozície myši
- najprv vo formulári klikneme na Image2 a potom v Inšpektore Objektov
(F11) prepneme záložku (kartu) Events a dvojklikneme
na pravú časť riadka s OnMouseMove
- pripraví sa procedúra TForm1.Image2MouseMove,
do ktorej napíšeme:
spracovanie udalosti pri každom
pohnutí myši nad plochou Image2:
|
procedure TForm1.Image2MouseMove(Sender:TObject; Shift:TShiftState; X,Y:Integer);
var
g1,g2:TCanvas;
begin
g1:=Image1.Canvas; g2:=Image2.Canvas;
g2.Pixels[x,y]:=g1.Pixels[x,y];
end;
|
- pričom X a Y sú súradnice bodu, nad
ktorým sa nachádza kurzor myši
- procedúru vylepšíme tak, že sa prekopíruje
aj malé okolie bodu (X,Y):
vylepšená procedúra:
|
procedure TForm1.Image2MouseMove(Sender:TObject; Shift:TShiftState; X,Y:Integer);
const
d=3;
var
g1,g2:TCanvas;
i,j:integer;
begin
g1:=Image1.Canvas; g2:=Image2.Canvas;
for i:=X-d to X+d do
for j:=Y-d to Y+d do
g2.Pixels[i,j]:=g1.Pixels[i,j];
end;
|
- v Inšpektore Objektov môžeme jednoducho zmeniť
aj popisy tlačidiel, napr. takto: vo formulári
klikneme na Button1 (tým sa oselektuje), v
inšpektore prepneme záložku Properties a
zmeníme pravú časť riadka Caption na ľubovoľný
text, napr. Tiger
- môže sa vám stať, že grafická
plocha pri každom prekresľovaní veľmi bliká,
vtedy pomôže riadok, ktorý pridáme
do procedúry FormCreate (táto
procedúra sa vytvorí dvojkliknutím
do formulára na prázdne miesto bez
komponentu):
aby grafické plochy neblikali:
|
procedure TForm1.FormCreate(Sender: TObject);
begin
DoubleBuffered:=true;
end;
|
NDÚ:
- pri kopírovaní pixelov môžete
posúvať začiatok každého riadka o nejakú
vypočítanú hodnotu, napr. pomocou funkcie sin
- obrázky môžete preklápať
podľa rôznych osí
- obrázok sa dá zväčšovať/zmenšovať,
resp. kopírovať viackrát na rôzne
pozície; v nejakej časti zahusťovať a pod.
- je zaujímavé pohrať sa s farbami
- robiť rôzne jasy, stmavovať obrázok
ku okrajom plochy, zvýrazňovať len nejakú
farebnú zložku (napr. "dozelena")
- z obrázka prekopírovať len tie
pixely, ktoré sú od nejakého
bodu (napr. (150,150)) nie ďalej ako napr. 100 --
ostatné pixely zafarbiť na bielo
- otočiť obrázok Image1 o 90 stupňov
- otočenie Image1 na svojom mieste
tak, že sa pritom
nepoužijete Image2 (ani iný TImage) a ani
iná dátová štruktúra
(napr. pomocné pole)
Príklad s korytnačkou a editovacím
riadkom
- vytvoríme novú aplikáciu - do
formulára položíme grafickú plochu
(Image1) a pod ňu editovací riadok (komponent
Edit1 zo štandardnej palety komponentov):
- naprogramujeme takéto správanie:
- v grafickej ploche bude korytnačka a túto
budeme riadiť príkazmi, ktoré sa zapisujú
do editovacieho riadka
- v Inšpektore objektov sa nastavíme na komponent
Edit1, prepneme Udalosti (Events) a dvojklikneme OnKeyPress
- vytvorí sa metóda Edit1KeyPress - táto
metóda sa zavolá vždy, keď sa bude niečo
zapisovať do editovacieho riadka - nás však bude
zaujímať len kláves <Enter> s kódom
#13
- program bude rozpoznávať túto množinu
príkazov:
- dopredu vlavo vpravo ph pd zmenfp zmenhp
- parameter príkazu je od neho oddelený
aspoň jednou medzerou
- Edit1.Text je momentálny obsah editovacieho
riadka
- štandardná funkcia StrToIntDef prevedie reťazec
na číslo a ak reťazec nie je správne zadané
celé číslo, tak vráti druhý
parameter funkcie (tzv. náhradnú hodnotu
- default)
- pomocou const môžeme definovať aj pole konštánt
- za identifikátor konštanty napíšeme
definíciu poľa a za znamienko rovnosti do zátvoriek
postupne vymenujeme všetky konštanty
program:
|
var
k:TKor;
procedure TForm1.FormCreate(Sender: TObject);
begin
k:=TKor.Create;
Edit1.Text:='';
end;
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
const
tab:array[1..7] of string =
('dopredu','vlavo','vpravo','ph','pd','zmenfp','zmenhp');
farby:array[0..7] of TColor =
(clBlack,clBlue,clRed,clGreen,clGray,clNavy,clYellow,clWhite);
var
s:string;
i,p:integer;
begin
if Key<>#13 then exit;
s:=Edit1.Text;
i:=pos(' ',s+' ');
p:=StrToIntDef(copy(s,i+1,MaxInt),0);
s:=LowerCase(copy(s,1,i-1));
i:=1; while (i<=high(tab)) and (tab[i]<>s) do inc(i);
with k do
case i of
1: Dopredu(p);
2: Vlavo(p);
3: Vpravo(p);
4: PH;
5: PD;
6: if (p>=low(farby)) and (p<=high(farby)) then
ZmenFP(farby[p]);
7: ZmenHP(p);
else exit;
end;
Edit1.Text:='';
end;
|
- program vylepšíme tak, že zabezpečíme, aby bolo korytnačku vidieť - nakreslíme
ju zeleným trojuholníkom - trojuholník
bude natočený v smere natočenia korytnačky
- vždy, keď sa korytnačka pohne alebo otočí,
prekreslí sa celá grafická plocha
najprv bez korytnačky
a na záver ju dokreslíme v novej polohe
(pomocná procedúra kresli)
- budeme
si pamätať postupnosť všetkých doterajších príkazov
(pole prikazy)
- príkaz zmaz vyprázdni túto
zapamätanú postupnosť
program:
|
var
k:TKor;
prikazy:array[1..1000] of record prikaz,param:integer; end;
pocet:integer = 0;
procedure kresli;
const
farby:array[0..7] of TColor =
(clBlack,clBlue,clRed,clGreen,clGray,clNavy,clYellow,clWhite);
var
i:integer;
begin
with k do begin
FP:=clBlack; HP:=1; ZmenXY(sirka/2,vyska/2); Smer:=0; PD;
zmaz;
for i:=1 to pocet do
with prikazy[i] do
case prikaz of
1: Dopredu(param);
2: Vlavo(param);
3: Vpravo(param);
4: PH;
5: PD;
6: if (param>=low(farby)) and (param<=high(farby)) then
ZmenFP(farby[param]);
7: ZmenHP(param);
8: pocet:=0;
end;
// nakreslí rovnoramenný zelený trojuholník
FP:=clLime; HP:=3; PD;
Vpravo(90); Dopredu(8); Vlavo(105); Dopredu(30.9);
Vlavo(150); Dopredu(30.9); Vlavo(105); Dopredu(8);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
k:=TKor.Create;
Edit1.Text:='';
kresli;
end;
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
const
tab:array[0..7] of string =
('zmaz','dopredu','vlavo','vpravo','ph','pd','zmenfp','zmenhp');
var
s:string;
i,p:integer;
begin
if Key<>#13 then exit;
s:=Edit1.Text;
i:=pos(' ',s+' ');
p:=StrToIntDef(copy(s,i+1,MaxInt),0);
s:=LowerCase(copy(s,1,i-1));
i:=0; while (i<=high(tab)) and (tab[i]<>s) do inc(i);
if i>high(tab) then exit;
if i=0 then pocet:=0 // príkaz zmaz
else begin
inc(pocet); prikazy[pocet].prikaz:=i; prikazy[pocet].param:=p;
end;
kresli;
Edit1.Text:=''; Key:=#0; // editovací riadok sme úspešne spracovali
end;
|
NDÚ
- namiesto editovacieho riadka použite textovú
plochu, do ktorej sa zapisuje celá postupnosť
korytnačích príkazov a celá
sa spracováva až do konca alebo po prvý
nedokončený, resp. nesprávny príkaz
|