26.9.2002
|
Logické hodnoty, logické výrazy
- najčastejšie slúži na vetvenie
výpočtu: cyklus s podmienkou na začiatku
alebo na konci (while, repeat) alebo
pre podmienený príkaz (if)
- vždy je buď pravdivý alebo nepravdivý,
má jednu z dvoch logických hodnôt
true alebo false
- logickú hodnotu vrátia napr. relácie:
<, <=, =, <>, >=, >
- logické operátory: not,
or, and, xor
precendencie operátorov (zatiaľ
nie sú všetky):
|
not *,
/, div, mod, and +, –, or, xor =,
<>, <, >, <=, >=, in
|
unárne multiplikatívne aditívne relačné
|
- každý výraz sa vyhodnocuje zľava
doprava
- problém napr. s d>=0
and d<=100
má byť: (d>=0)
and (d<=100)
While - cyklus = cyklus s podmienkou na začiatku
- otestuje sa podmienka a ak je
pravdivá (true), tak sa vykoná
príkaz (t.j. telo cyklu) a
celé sa to opakuje
- ak bude podmienka nepravdivá
(false), tak sa telo cyklu už nevykoná
a pokračuje sa na príkaze za cyklom
- ak podmienka bude mať stále
hodnotu true, tak vznikne nekonečný
cyklus
- pod Windows s tým môžu byť
obrovské problémy - niekedy
treba reštartovať celý systém
(napr. while
true do; )
- ak nekonečný cyklus (alebo veľmi
dlho trvajúci cyklus) neobsahuje špeciálnu
vsuvku pre Windows, tak počas cyklu sa systém
nedostane ku slovu a tým sa napr. normálne
nedá
zhodiť bežiaci program
- aby sa Windows dostal ku slovu, telo cyklu
môžeme spomaliť procedúrou cakaj(1);
- iným riešením je raz začas
použiť volanie Application.ProcessMessages;,
ktoré pustí Windows na chvíľu
ku slovu a tým dovolí používateľovi
ukončiť aplikáciu alebo zatlačiť tlačidlo
a pod. - aj toto volanie ale trochu spomalí
priebeh výpočtu
použitie cyklu:
|
procedure TForm1.Button1Click(Sender: TObject);
var
k:TKor;
d:integer;
begin
k:=TKor.Create;
d:=5;
while d<500 do begin
k.dopredu(d); k.vlavo(160);
d:=d+2;
cakaj(1); // Application.ProcessMessages;
end;
end;
|
použitie nekonečného cyklu
while true do:
|
var
k:TKor;
d,uhol:integer;
begin
k:=TKor.Create;
while true do begin
zmaz; uhol:=90+random(90); // náhodné číslo od 90 do 179
d:=5; k.PresunXY(250,200);
while d<500 do begin
k.dopredu(d); k.vlavo(uhol);
d:=d+2;
end;
cakaj(1000);
end;
end;
|
Poznámka:
- posledný príklad je zaujímavý
tým, že takáto aplikácia sa ťažšie
ukončuje -- akcia, ktorá sa spúšťa
po zatlačení tlačidla (napr. Button1Click)
vďaka nekonečnému cyklu nikdy nekončí
- takúto aplikáciu môžeme
zastaviť z prostredia Delphi pomocou voľby Program
Reset (Ctrl+F12) v menu Run
- druhou možnosťou je použitie udalosti
onClose na formulári:
- klikneme na nejaké voľné
miesto na formulári Form1 tak, aby
sa v Inšpektore objektov v hornej časti
objavil text Form1 TForm1
- klikneme na záložku Events
a potom dvojklikneme na voľné políčko
v riadku onClose
- v našom programe sa pripraví
procedúra FormClose, do ktorej dopíšeme
príkaz halt
- Chceli by sme vylepšiť test ukončenia cyklu tak,
aby skončil, ak sa korytnačka vzdiali od (250,200) aspoň
o 200 (k.X a k.Y je spôsob ako zistiť jej
momentálne súradnice):
while sqrt((k.X-250)*(k.X-250)+(k.Y-200)*(k.Y-200))<200 do
... while sqrt(sqr(k.X-250)+sqr(k.Y-200))<200 do ...
alebo využijeme korytnačiu funkciu vzd, ktorá
vypočíta vzdialenosť korytnačky od zadaného
bodu v rovine:
while k.Vzd(250,200)<200 do ...
cyklus skončí,
keď sa korytnačka priveľmi vzdiali od štartového
bodu: |
d:=5; k.PresunXY(250,200);
while k.Vzd(250,200)<200 do begin
k.dopredu(d); k.vlavo(uhol);
d:=d+2;
end;
|
For-cyklus vlastne nepotrebujeme, lebo sa dá
prepísať pomocou while:
napr.
for-cyklus: |
for i:=1 to 10 do k.dopredu(i);
|
môžeme prepísať pomocou while:
|
i:=1;
while i<=10 do begin
k.dopredu(i);
i:=i+1
end;
|
Náhodné prechádzky
- Necháme korytnačku do "nekonečna"
sa prechádzať po obrazovke: náhodne sa
otočí o nejaký uhol a vykročí o
nejakú konštantnú vzdialenosť. Používame
štandardnú funkciu random(
číslo ), ktorá
zakaždým vráti inú náhodnú
hodnotu od 0 až po číslo-1. Nekonečný
cyklus robíme pomocou stále pravdivého
logického výrazu true (môžeme ho
ale ukončiť uzavretím projektu).
náhodná
prechádzka: |
var
k:TKor;
begin
k:=TKor.Create(250,200);
while true do begin
k.vpravo(random(360));
k.dopredu(5);
Application.ProcessMessages;
end;
end;
|
- Korytnačka veľmi rýchlo ujde z plochy preč.
Pokúsime sa preto strážiť korytnačku,
aby nevyšla z nejakej oblasti, t.j. ak aj vyjde, tak
ju ihneď vrátime späť. Použijeme nový
príkaz vetvenia if (má dva varianty, zatiaľ
ten jednoduchší):
podobne ako while, ak je podmienka splnená
vykoná sa vnorený príkaz, ak je
hodnota podmienky nepravda, tak sa vnorený príkaz
nevykoná ale preskočí:
náhodná
prechádzka - korytnačka urobí
krok späť, keď sa priveľmi vzdiali
od štartového bodu: |
var
k:TKor;
begin
k:=TKor.Create(250,200);
while true do begin
k.vpravo(random(360));
k.dopredu(5);
if k.vzd(250,200)>50 then
k.dopredu(-5);
Application.ProcessMessages;
end;
end;
|
- Vyskúšajte si niekoľko variantov podmienky
a trénujte si aj kombinácie podmienok
pomocou logických operátorov and a or
(treba si uvedomiť, že podmienka v týchto príkladoch
vyjadruje, že korytnačka opustila stráženú
oblasť, t.j. že je zle a treba sa vrátiť):
if (abs(k.X-250)>100)or(abs(k.Y-200)>50)
then ...
- zjednotenie troch kružníc (snehuliak)
if (k.vzd(250,70)>80)and(k.vzd(250,200)>50)and(k.vzd(250,280)>30)
then ...
snehuliak - namiesto
Application.ProcessMessages môžeme použiť Image1.Repaint: |
var
k:TKor;
begin
k:=TKor.Create(250,200);
k.FP:=clWhite; zmaz(clLtGray);
while true do begin
k.vpravo(random(360));
k.dopredu(5);
if (k.vzd(250,60)>30)and(k.vzd(250,140)>50)and(k.vzd(250,270)>80) then
k.dopredu(-5);
Image1.Repaint;
end;
end;
|
- v tomto príklade sme vyhodili Application.ProcessMessages;
- aby sme vôbec niečo v grafickej ploche videli,
musíme pridať zobrazenie grafickej plochy,
napr. pomocou Image1.Repaint;
- uvedomte si, že teraz bude trochu komplikovanejšie
ukončiť takýto bežiaci projekt, lebo tu nám
nepomôže ani halt vo FormClose
NDÚ
- "stráženú" náhodnú
prechádzku opravte tak, aby sa korytnačka
pohybovala len v oblasti:
- mesiac ako rozdiel dvoch kružníc
- slovenský dvojkríž ako zjednotenie
troch obdĺžnikov
- elipsa
- preprogramujte predchádzajúce
príklady tak, aby korytnačka nevystúpila
zo stráženej oblasti a to tak, že so zdvihnutým
perom otestuje krok dopredu, a vráti sa späť.
Ak to bolo OK, tak spraví krok so spusteným
perom, inak nerobí nič a pokúša sa
ísť iným smerom
- naprogramujte paralelne 2 korytnačky, každá
nech má svoju chránenú oblasť
(môžu kresliť aj inou farbou)
- naprogramujte paralelne 3 korytnačky vytvorené
v strede plochy s chránenou oblasťou kruhom s
polomerom 50, pričom každá korytnačka má hrubé
pero s farbami žltá, modrá a zelená
a pohybujú sa krokom 3
Procedúry - podprogramy
- je to pomenovanie nejakého algoritmu,
nejakej časti programu s možnosťou vyvolať túto
časť pomocou príslušného mena
- pomocou podprogramov môžeme riešiť problém
jeho rozdelením na podúlohy ->
riešenie zhora nadol
- v pascale môžu byť podprogramy
- procedúry
- funkcie – vrátia nejakú hodnotu
- majú výhodu, že sú viacnásobne
použiteľné
- neskôr uvidíme aj rekurziu
Deklarácie procedúr
- Príklad: vytvoríme procedúru
na kreslenie štvorca – všimnime si, že var k:TKor musí
byť deklarované ešte pred procedúrou,
aby táto mohla pracovať s korytnačkou:
procedúra stvorec
je vnorená v TForm1.Button1Click: |
procedure TForm1.Button1Click(Sender: TObject);
var
k:TKor;
procedure stvorec;
var
i:integer;
begin
for i:=1 to 4 do begin
k.dopredu(100); k.vpravo(90);
end;
cakaj(100);
end;
begin
k:=TKor.Create(250,200);
stvorec;
k.PresunXY(300,280);
stvorec;
k.PresunXY(120,190);
stvorec;
end;
|
Ako pracuje počítač pri volaní procedúry:
- keď sa objaví v programe meno procedúry
(musela byť definovaná v deklaráciách),
ide o volanie tejto procedúry, t.j.
- zapamätá sa návratová
adresa (kam sa bude treba vrátiť)
- vytvoria sa lokálne premenné procedúry
(s nedefinovanou hodnotou)
- neskôr uvidíme,
že tu sa vytvoria aj formálne
parametre
- prenesie sa riadenie programu do tela podprogramu
- vykonajú sa všetky príkazy podprogramu
- zrušia sa lokálne premenné
- riadenie sa vráti za miesto v programe, odkiaľ
bol podprogram volaný
pre pascal platí
- identifikátor sa môže používať,
až keď bol deklarovaný
- štandardné deklarácie (štandardné
typy, funkcie, podprogramy) – sú definované
ako keby ešte pred programom == 0. úroveň
- predefinovanie je zakázané na
tej istej úrovni, ale povolené vo
vnorených úrovniach
- napr. var real:integer;
- identifikátor je viditeľný len
na svojej úrovni a na všetkých vnorených
úrovniach, kde nie je predefinovaný
- definované procedúry sú
na vyšších úrovniach ako náš
"program" – oni vidia deklarácie
tohoto programu (ak boli pred ich definíciou)
ale samotný program nevidí dovnútra
procedúr
- procedúra stvorec vidí premennú
k (korytnačku) ale program nevidí premennú
i definovanú v tejto procedúre
stvorec
v danom programe:
- na 0. úrovni sú všetky štandardné
identifikátory a unit KorUnit (teda aj typ
TKor, procedúra zmaz a pod.)
- na 1. úrovni je definícia formuláru
TForm1 ale aj
procedure TForm1.Button1Click(Sender: TObject); do ktorej píšeme náš program
- na 2. úrovni je premenná k a procedúra
stvorec
- na 3. úrovni je lokálna premenná
i
Formálne parametre
- pri volaní podprogramu mu môžeme poslať
nejaké hodnoty, aby sme nemuseli rôzne
špeciality nastavovať v globálnych premenných
(napr. veľkosť kresleného štvorca): v podprograme
zadefinujeme špeciálne lokálne premenné,
tzv. formálne parametre, do ktorých sa
pred samotným volaním priradia (inicializujú)
hodnoty skutočných parametrov:
procedúra stvorec
s parametrom - veľkosť strany štvorca: |
procedure TForm1.Button1Click(Sender: TObject);
var
k:TKor;
procedure stvorec(velkost:integer); // velkost – formálny parameter
var
i:integer;
begin
for i:=1 to 4 do begin
k.dopredu(velkost); k.vpravo(90);
end;
cakaj(100);
end;
var
j:integer; // mohol by to byť aj identifikátor i
begin
k:=TKor.Create;
j:=10;
while j<=200 do begin
stvorec(j); // hodnota výrazu j je skutočný parameter
k.vlavo(8);
j:=j+10;
end;
end;
|
Podprogram môže mať aj viac parametrov, buď
rovnakého alebo rôznych typov:
|
procedure TForm1.Button1Click(Sender: TObject);
var
k:TKor;
procedure poly(n,d,u:integer);
var
i:integer;
begin
for i:=1 to n do begin
k.dopredu(d); k.vpravo(u);
end;
end;
begin
k:=TKor.Create;
k.PresunXY(50,200);
poly(360,2,1);
k.PresunXY(150,170);
poly(5,150,144);
k.PresunXY(350,200);
poly(4,100,90);
k.PresunXY(345,200);
poly(4,100,-90);
k.PresunXY(350,195);
poly(4,-100,-90);
k.PresunXY(345,195);
poly(4,-100,90);
end;
|
NDÚ
- napíšte procedúru dom s parametrom
velkost, ktorá nakreslí domček zložený
so štvorca a rovnostranného trojuholníka
len pomocou volaní procedúry poly
(žiadne dopredu ani otáčania)
- napíšte dom tak, aby začal v ľavom dolnom
rohu a skončil v pravom dolnom a pomocou dom naprogramujte
ulica, ktorá nakreslí n náhodne
veľkých domov vedľa seba
- zadefinujte procedúru lupen, ktorá
nakreslí lupienok zložený z dvoch
štvrťkružníc (za kružnicu môžeme považovať
napr. pravidelný 24-uholník) a ďalej
procedúru kvet, ktorá nakreslí
kvet zložený z n lupienkov nejakej veľkosti
a farby
Základné jednoduché typy
Ďalej budeme potrebovať robiť poly aj pre iné
ako celé
hodnoty dĺžky, resp. uhla (napr. pre pravidelný
7-uholník)
Musíme teda používať nový typ
– real. Pascal je prísne typový jazyk:
- každá premenná musí mať dopredu
určený svoj typ, nedá sa počas behu meniť
- každá operácia pracuje len s určitým
typom, napr. /, div
- existuje výnimka - automatická konverzia
(integer -> real)
Zhrňme doterajšie poznatky o integer, aby sme lepšie
pochopili real
Integer
- hodnoty sú z intervalu -2147483648 .. 2147483647
- zaberá 4 bajty = 32 bitov, 1bit znamienko
- definované sú aritmetické operácie
+, -, *, div, mod, obidva operandy sú celé
čísla
- priraďovací príkaz, na oboch stranách
celé čísla
- relačné operácie <, <=, ...
- pred, succ - funkcie
- inc, dec - príkazy
- štandardné funkcie, napr. sqr, abs
- šestnástkové konštanty - $, napr. $1e=30
Real
- čísla vyjadrené desatinným zápisom
aj s exponentom
- obsahuje des. bodku, exponent približne ±300
- real ± (5.0 x 10^–324 .. 1.7 x 10^308 ) 15-16
číslic 8 bajtov
- ukážky reálnych čísel 1.23,
-3.23989E+02, 9.89E-01
- pre každé číslo nevieme určiť nasledovné
reálne číslo,
t.j. medzi 0 a 0.0001 možno existuje číslo,
nemá zmysel nasledovník, t.j. nefunguje pred ani succ
- operácie - vyzerajú rovnako, ale sú
to iné operácie, robia s reálnymi
číslami +, -, *, / (oba operandy sú reálne)
- automatická konverzia - keď sa pri reálnej
operácii vyskytne celočíslený operand
konvertuje sa na reálne číslo, napr. 1+1.5,
1/3 = 1.0/3.0
- analogicky s relačnými a s priradením,
real:=integer, ale nie integer:=real !!
- nefunguje ani inc, dec
- reálne operácie veľmi často majú
chyby spôsobené realizáciou v počítači
- štandardné funkcie, napr. sqrt, sqr, sin,
cos, abs
Boolean
- konštanty - predefinované identifikátory
konštanty true, false (ako predtým definované
naše konštanty)
- v pamäti = 1 bajt
- operácie and, or, not, xor
- všetky relácie z predchádzajúcich
typov - ich výsledkom je logická hodnota
- relácie
- FALSE<TRUE, FALSE<=TRUE, TRUE<=TRUE
- priradenie - neexistuje konverzia
- štandardné funkcie: ord, boolean( 0 alebo
1), napr. boolean(0)=FALSE
- pred, succ
opravená procedúra poly s reálnymi
parametrami:
|
procedure TForm1.Button1Click(Sender: TObject);
var
k:TKor;
procedure poly(n:integer; d,u:real);
begin
while n>0 do begin
k.dopredu(d); k.vpravo(u); dec(n);
end;
end;
var
i:integer;
begin
k:=TKor.Create(150,250);
for i:=3 to 10 do
poly(i,100,360/i); // !!! reálne delenie
end;
|
Viac tlačidiel a tlačidlo s viac stavmi
- V príklade, v ktorom sme demonštrovali použitie
procedúry poly, sme viackrát volali túto
procedúru - môžeme to urobiť aj zadefinovaním
viacerých tlačidiel, pričom pod každým
tlačidlom bude kreslenie iného útvaru.
korytnačka je tu definovaná
ako globálna premenná:
|
var
k:TKor; // korytnačka je tu globálna, aby ju "videli" všetky procedúry
procedure poly(n,d,u:integer);
var
i:integer;
begin
for i:=1 to n do begin
k.dopredu(d); k.vpravo(u);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
k:=TKor.Create;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
poly(360,2,1);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
poly(5,150,144);
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
poly(4,100,90);
end;
...
|
- Problém, ktorý tu treba vyriešiť
je ten, že by sa nám hodilo, aby sa korytnačka
automaticky vytvorila pri štarte programu a nie
až po zatlačení tlačidla Button1 (každé
tlačidlo by mohlo mať svoju korytnačku, ale to má
svoje nevýhody).
korytnačka sa vytvorí: |
var
k:TKor;
procedure TForm1.FormCreate(Sender: TObject);
begin
k:=TKor.Create;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
k.Dopredu(50); k.Vpravo(90);
end;
...
|
- Ešte ukážeme, ako využiť jediné
tlačidlo, ktoré postupne bude mať rôzne
funkcie - postupne sa bude meniť stav tohoto tlačidla:
jedno tlačidlo vykonáva
rôzne akcie: |
var
stav:integer = 0; // premenná stav je inicializovaná na 0
k:TKor;
procedure poly(n,d,u:integer);
var
i:integer;
begin
for i:=1 to n do begin
k.dopredu(d); k.vpravo(u);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if stav=0 then begin
k:=TKor.Create; stav:=1;
Button1.Caption:='kružnica';
end else if stav=1 then begin
poly(360,2,1); stav:=2;
Button1.Caption:='hviezda';
end else if stav=2 then begin
zmaz; poly(5,150,144); stav:=3;
Button1.Caption:='štvorec';
end else if stav=3 then begin
zmaz; poly(4,100,90); stav:=4;
end else if stav=4 then begin
poly(4,-100,90); stav:=5;
end else if stav=5 then begin
poly(4,100,-90); stav:=6;
end else if stav=6 then begin
poly(4,-100,-90); stav:=7;
Button1.Caption:='zmaž';
end else if stav=7 then begin
zmaz; stav:=1;
Button1.Caption:='kružnica';
end;
end;
|
- všimnite si spôsob naformátovania
vnorených príkazov if - niektorí
programátori preferujú práve
takéto
formátovanie
- tiež si všimnite, že premenné k aj stav musia
byť globálne, inak (keby boli niekde lokálne)
by sa zabúdala ich predchádzajúca
hodnota
- použili sme novinku:
- Button1.Caption:='kružnica'; - zmení
sa text na tlačidle
- s tlačidlom by sme v budúcnosti mohli
využiť napr. aj toto
- Button1.Enabled:=false; - zablokuje sa tlačidlo
- Button1.Enabled:=true; - tlačidlo sa odblokuje
- znovu sa môže tlačiť
- to isté zapíšeme elegantnejšie
pomocou zloženého príkazu case - podľa
nejakej hodnoty sa vykoná jeden z možných
príkazov (alebo žiadny):
použitie vetvenia case: |
procedure TForm1.Button1Click(Sender: TObject);
begin
case stav of
0:
begin
k:=TKor.Create; stav:=1;
Button1.Caption:='kružnica';
end;
1:
begin
poly(360,2,1); stav:=2;
Button1.Caption:='hviezda';
end;
2:
begin
zmaz; poly(5,150,144); stav:=3;
Button1.Caption:='štvorec';
end;
3:
begin
zmaz; poly(4,100,90); stav:=4;
end;
4:
begin
poly(4,-100,90); stav:=5;
end;
5:
begin
poly(4,100,-90); stav:=6;
end;
6:
begin
poly(4,-100,-90); stav:=7;
Button1.Caption:='zmaž';
end;
7:
begin
zmaz; stav:=1;
Button1.Caption:='kružnica';
end;
end;
end;
|
- pričom vetva else (podobne ako pri if)
môže chýbať
- výraz aj konštanty
môžu byť len celočíselného typu
(mohli by byť aj logického typu, ale toto
zrejme veľký význam nemá; neskôr
uvidíme aj iné prípustné
typy)
|