8.) Írjunk Unit-ot, mely
tartalmazza a Boritek program eljárásait.
Ha jól megnézzük, a Boritek
programban deklarált eljárásokat, azok bizony olyanok, hogy több programban is
használhatónak látszanak. Hogyan lehetne azt elérni, hogy minden program
elejére ne kelljen újra és újra ezeket az eljárásokat
leírni, de használni mégis lehessen őket. Erre találták ki a Pascal fejlesztői a Unit-okat.
Erről már a Crt
unit kapcsán szó volt, de nemcsak a fejlesztők által megírt unitok létezhetnek,
mi magunk is írhatunk unitokat. Ez lesz számunkra a kód-újrafelhasználás
leggyakoribb példája. Nézzük, hogyan néz ki egy Unit felépítése:
Unit CrtPlus;
InterFace
Uses NewDelay, Crt;
Implementation
End.
A unit neve most CrtPlus, mely kötelezően meg kell, hogy egyezzen a DOS nevével.
A névválasztás arra utal, hogy a standard Crt Unit
kiegészítését célozza. Az InterFace kulcsszó után
kell beírni a Unit-ban használt unitok nevét, valamint a Unit azon eljárásainak a nevét, amelyet
a használó programból el szeretnénk érni. Az Implementation szó után kell
elhelyezni az eljárások kifejtését. Ezt mutatja a teljes lista:
Unit CrtPlus;
InterFace
Uses NewDelay, Crt;
Procedure WriteXY(X,Y: Byte; Sz: String);
Procedure VVonal(Xk,Xv,Y:
Byte);
Procedure FVonal(X,Yk,Yv:
Byte);
Procedure Keret(Bfx,Bfy,Jax,Jay:
Byte);
Implementation
Procedure WriteXY(X,Y: Byte; Sz: String);
Begin
GoToXY(X,Y);
Write(Sz);
End;
Procedure VVonal(Xk,Xv,Y:
Byte);
Var I: Byte;
Begin
For i:= Xk To Xv Do
WriteXY(I,Y,Chr(196));
End;
Procedure FVonal(X,Yk,Yv:
Byte);
Var I: Byte;
Begin
For I:= Yk To Yv Do
WriteXY(X,I,Chr(179));
End;
Procedure Keret(Bfx,Bfy,Jax,Jay:
Byte);
Begin
VVonal(Bfx+1,Jax-1,Bfy);
VVonal(Bfx+1,Jax-1,Jay);
FVonal(Bfx,Bfy+1,Jay-1);
FVonal(Jax,Bfy+1,Jay-1);
WriteXY(Bfx,Bfy,Chr(218));
WriteXY(Jax,Bfy,Chr(191));
WriteXY(Jax,Jay,Chr(217));
WriteXY(Bfx,Jay,Chr(192));
End;
End.
Mint látható, a listát
két End
zárja, egyik az utolsónak leirt eljárás End-je, a másik a Unit End-je. Unit csak önállóan szerkeszthető, programon belül nem. Használata
előtt még le kell fordítani, méghozzá lemezre. Ezt a Compile
menü Destination almenüpontjában állíthatjuk be. A
lemezen nem *.exe, hanem *.tpu állomány jön létre. (TPU: Turbo
Pascal Unit).
A meglévő Unit-unkat bővítsük még a következőkkel: gyakran kell a
háttér és a betűszint beállítani, lehessen ezt egy Szinek(HSzin,KSzin) eljárással megadni.
Procedure Szinek(HSzin,KSzin:
Byte);
Begin
TextBackGround(HSzin);
TextColor(KSzin);
End;
Illesszük a Szinek
eljárást a Unitba első eljárásként:
Unit CrtPlus;
InterFace
Uses NewDelay, Crt;
Procedure Szinek(HSzin,KSzin: Byte);
Procedure WriteXY(X,Y: Byte; Sz: String);
Procedure VVonal(Xk,Xv,Y:
Byte);
Procedure FVonal(X,Yk,Yv:
Byte);
Procedure Keret(Bfx,Bfy,Jax,Jay:
Byte);
Implementation
Procedure Szinek(HSzin,KSzin:
Byte);
Begin
TextBackGround(HSzin);
TextColor(KSzin);
End;
...
End.
A továbbiakban, ha olyan
eljárást írunk, amely több programban is használható, újra így járjunk el. Ne
felejtsük újra lemezre lefordítani a Unit-ot. Nézzük meg, a CrtPlus
segítségével hogyan néz ki a Boritek listája:
Program Boritek;
Uses NewDealay, Crt, CrtPlus;
Begin
TextMode(CO80);
Szinek(Blue,Yellow);
ClrScr;
Keret(1,1,80,24);
Keret(69,2,78,6);
Keret(4,17,15,19);
Keret(55,21,57,23);
Keret(59,21,61,23);
Keret(63,21,65,23);
Keret(67,21,69,23);
VVonal(7,30,3);
VVonal(7,25,5);
VVonal(20,60,12);
VVonal(55,76,17);
VVonal(55,74,19);
WriteXY(2,2,'F.a.:');
WriteXY(6,18,'AJÁNLOTT');
WriteXY(71,4,'BÉLYEG');
GotoXY(1,25);
ReadLn;
End.
Unit-unk eljárásait tehát csak
akkor használhatjuk, ha magát a Unit-ot
használatba vettük. Ez történt a program második sorában: Uses Crt,
CrtPlus;.
9.) Irjunk programot, melyet futtatva a gép véletlen méretű és szinű keretes, cimkés, árnyékos
ablakokat rajzol a képernyőre, közben változó magasságú hangot ad. A program
futása bármely billentyű megnyomására álljon le.
Gyakran fordul elő
felhasználói programokban, hogy a képernyőn keretes ablak jelenik meg, melyben
a gép valamire figyelmeztet, esetleg valamilyen választást kínál fel, vagy
menüt. Szépen megírt programjainkban nekünk is
szükségünk lehet ilyen ablakokra többször is, így célszerű az ablak eljárást a CrtPlus Unit-unkban megírni.
Hogyan is néz ki egy ablak? Már rajzoltunk téglalapot is, keretet is. Ha ezeket
„egymásra” helyezzük, akkor keretes ablakot kapunk, legyen az ablaknak címkéje,
és igény szerint árnyéka is. Helyezzük ezt az eljárást utolsó helyre a CrtPlus-ban.
Unit CrtPlus;
InterFace
Uses NewDelay, Crt;
...
Procedure Ablak(HSz,KSz,Bfx,Bfy,Jax,Jay: Byte; Arny: Boolean; C: String);
Implementation
...
Procedure Ablak(HSz,KSz,Bfx,Bfy,Jax,Jay: Byte; Arny: Boolean; C: String);
Var Px,Py: Byte;
Begin
If Arny Then
Begin
Px:= Jax+2;
Py:= Jay+1;
If Px>80
Then Px:= 80;
If Py>25
Then Py:= 25;
Window(Bfx+2,Bfy+1,Px,Py);
Szinek(Black,Black);
ClrScr;
End;
Window(Bfx,Bfy,Jax,Jay);
Szinek(HSz,KSz);
ClrScr;
Window(1,1,80,25);
Keret(Bfx+1,Bfy,Jax-1,Jay);
If C<>'' Then WriteXY(Round(Bfx+(Jax-Bfx-Length(C))/2-1),Bfy,' '+C+' ');
End;
End.
Az Ablak eljárás készítésekor szükség van egy tesztprogramra, melyet
minden módosítás után futtathatunk, és így láthatjuk a kialakítás helyes
menetét.
Program AblTeszt;
Uses NewDelay, Crt, CrtPlus;
Begin
TextMode(CO80);
Szinek(Cyan,White);
ClrScr;
Ablak(15,0,
20,5,60,15, True, ’Ablak’);
GoToXY(1,25);
ReadLn;
End.
Az Ablak eljárás számos új dolgot tartalmaz. Az első mindjárt az Arny: Boolean deklaráció. A feladat azt kéri,
hogy lehessen az ablaknak árnyéka is, azaz, az eljárást ilyen szempontból
lehessen kétféleképpen meghívni, árnyékkal és árnyék nélkül is. Ezt úgy
érhetjük el, hogy az eljárás paraméterei közé felveszünk egy logikai változót,
ez az Arny,
melyet a Pascal Boolean típusnak nevez. Egy logikai
változó két értéket kaphat, illetve vehet fel, ezek: True vagy False, azaz Igaz vagy Hamis. Az
eljárásunknak biztosan lesz egy olyan szakasza, melyet a szerint kell vagy nem
végrehajtani, hogy kell-e árnyék vagy nem. Mivel az árnyék egy, az ablakhoz
képest jobbra és lefelé eltolt fekete téglalap, ezért ezt kell hamarabb
megrajzoltatni az eljárásban. Az eljárás tehát ezzel kezdődik, és itt a második
új dolog az If … Then … Else szerkezetű programszakasz.
A lista utasításainak végrehajtási sorrendjét (a szekvenciát), eddig a For ciklus (iteráció)
változtatta meg. De az If …Then …
szerkezet, vagyis a szelekció egyik fajtája is meghatározhatja. Az If azt jelenti:
ha, a Then:
akkor. Az If-fel
kezdődő programrészletet így olvashatjuk: Ha igaz, hogy kell árnyék, akkor a
következőt tedd: Begin … End;. A Then is csak egy utasításra
vonatkozik, mint a Do,
ezért, ha több mindent kell tenni, akkor összetett utasítást kell alkalmaznunk.
Az Else,
ami egyébként-et jelent itt nem alkalmaztuk, mert nem
volt rá szükség, az árnyék helyett nem kellett valami mást rajzolni. Lesz majd
a későbbiekben olyan program, amelyben szükség lesz rá. Maga az árnyékrajzolás
nagyon egyszerű, kiolvasható a jobbra kettővel, lefelé eggyel való ablakeltolás,
a fekete háttérszín és írásszín beállítása. (A későbbiekben lehet, hogy ezt nem
árt pontosítani, megvizsgálni, hogy az így keletkezett Window
ablak megnyitható-e, mert ha nem, akkor a ClrScr
másra vonatkozhat. De most ezzel nem bonyolítjuk az eljárásunkat.) Az eljárás
további része az ablakot rajzolja. Először megnyitja a megfelelő ablakot, és a
megadott színnel törli, majd visszaállítja a teljes képernyőt és a megnyitott
ablak területre egy szűkebb keretet rajzol (+1 és –1
értékek), így lesz keretes ablak a képernyőn. Még az utolsó sor is tartogat
újdonságokat. Ez csak akkor hajtódik végre, ha a C címke nem üres szöveg. A címkét
a keretre, annak felső részére, sőt középre illik helyezni. Ezt a pozíciót
határozza meg a WriteXY első paraméterében leírt
kifejezés. A kiírás helye az ablak (közepe-címke hossza)/2. A C címke hosszát a
Length függvény határozza meg, mely természetesen
bármely szöveges változóra alkalmazható. (A szöveges változókra vonatkozó
műveletekkel, eljárásokkal és függvényekkel egy külön szakasz foglalkozk.) A kettővel való osztás a változók típusát
valósra állítja, a WriteXY viszont Byte-ot, azaz egészet vár, ezért a Round
függvény, mely a benne lévő kifejezést kerekíti és
egész típusúra változtatja. Azért, hogy a címke betűjéhez a keretvonal ne érjen
hozzá előtte és utána még egy Space-t is írtunk, ez
eggyel csökkenti a kiírás X koordinátáját. A kiírandó szöveget összeadással, a String típusú változók között értelmezett egyetlen
művelettel oldottuk meg. Elkészült az ablak eljárásunk, most már nézhetjük a
kitűzött feladatot. A véletlen számok előállításának megismerésére, először
csak véletlen számokat (például lottószámokat) kiíró programot fogunk írni.
Program Ablakok;
Uses NewDelay, Crt, CrtPlus;
Begin
TextMode(CO80);
ClrScr;
Repeat
Write(Random(90)+1:3);
Delay(300);
Until KeyPressed;
End.
Megfigyelhetjük, hogy Randomize nélkül minden gép, mindig ugyanazon számokat
adja. A +
Program Ablakok;
Uses NewDelay, Crt, CrtPlus;
Begin
TextMode(CO80);
Repeat
Ablak(Random(8), Random(16), Random(80)+1, Random(25)+1,
Random(80)+1, Random(25)+1, true, ’’);
Sound(100*Random(80));
Delay(150);
Until KeyPressed;
NoSound;
End.
A program természetesen
használja a Crt és CrtPlus
unitot is. Azt, hogy egy programrészletet akárhányszor, azaz bizonytalan
sokszor ismételjen a program, nem lehet (vagy nem célszerű) a már ismert
ismétlő eljárással, a For
ciklussal megoldani. Olyan ismétlő eljárás kellene, amely leállító feltétele
tőlünk, egy programon kívüli tevékenységtől függ. Ilyen ismétlő eljárás a Repeat … Until …. Repeat: ismételd, Until:
mindaddig, míg nem. Az ismétlésre szánt programrészletet a két kulcsszó közé,
természetesen bekezdésesen kell leírni. Az ismétlő eljárás leállító feltételét
az Until
után kell írni. Az ismétlés mindaddig folytatódik, ameddig a feltétel igazzá
nem válik. Mivel a feltétel az ismételendő programrészlet után van, vagyis ez
egy hátul tesztelő ciklus, így a ciklusmag legalább egyszer végrehajtódik. A
feltétel valamilyen logikai kifejezés, vagy függvény, esetleg konstans. Most a
leállítás a billentyűzeten való írás, ezért olyan logikai függvény kell, amely hamis,
ha nem nyomtak meg billentyűt, (azaz a billentyűzet puffer üres), és igaz
ellenkező esetben. Ez a logikai függvény a KeyPressed
(= kulcs megnyomva).
Az ismétlés három
eljárás hívására vonatkozik: Ablak, Sound és Delay. Mindhárom tartalmaz ismeretlen dolgokat. Az Ablak
paramétereinek véletlen értékeket kell tartalmaznia. Véletlen számok előállítására
a Random függvény alkalmas. Paraméteresen használva: Random(n),
a paramétere egész, és visszaadott értéke 0 és n-1 közötti egész szám.
Paraméter nélkül a visszaadott érték 0 és 1 közötti valós szám. Ahhoz tehát,
hogy 1 és 80 közötti számot kapjunk a Random(80)-hoz még hozzá kell adni 1-et. (Ha
50 és 60 közötti véletlen számot akarunk, akkor Random(60-50+1)+
Gondolhatnánk, hogy
ezzel a feladatot megoldottuk. De azért ez nem teljesen van így. Ha jól
megnézzük a képernyőt futás közben, bizony furcsa dolgokat látunk, pl.:
változik a hang, a képernyő viszont nem. Ez hogy lehet? Miért nem rajzol néha a
program. Hát azért nem, mert az Ablak eljárást vizsgálat nélküli véletlen
paraméterekkel hívtuk meg. Így előfordul, hogy nem tudja a gép az ablakot
felrajzolni. A következő változat ezen próbál segíteni. Azt kellene elérni,
hogy a véletlen értékek nagyságrendje megfeleljen a hívási feltételeknek. Ezért
a választás után még meg is kell vizsgálni. Ez csak úgy lehetséges, ha az
értékeket változókba mentjük, sőt a cseréhez még egy ötödik változó is kell, ez
a Puf nevű. A másik gond az, hogy így mindig
ugyanazon értéksor szerint jönnek létre az ablakok, akárhányszor indítjuk el a
programot, sőt akármelyik gépen. Azért, hogy a véletlen, valóban véletlen legyen,
a véletlen függvény meghívása előtt meg kell hívni a Randomize
eljárást. Ezt láthatjuk a Repeat előtt.
Program Ablakok;
Uses NewDelay, Crt, CrtPlus;
Var Bfx,Bfy,Jax,Jay,Puf:
Byte;
Begin
TextMode(CO80);
Randomize;
Repeat
Bfx:= Random(80)+1;
Bfy:= Random(25)+1;
Jax:= Random(80)+1;
Jay:= Random(25)+1;
If Bfx>Jax Then
Begin
Puf:= Bfx;
Bfx:= Jax;
Jax:= Puf;
End;
If Bfy>Jay Then
Begin
Puf:= Bfy;
Bfy:= Jay;
Jay:= Puf;
End;
Ablak(Random(8),Random(16),Bfx,Bfy,Jax,Jay,
true, ’’);
Sound(100*Bfx);
Delay(150);
Until KeyPressed;
NoSound;
End.
A véletlen értékek tehát
a Bfx, Bfy, Jax, Jay változókba kerültek.
Külön teljesülni kell, hogy Bfx<Jax és Bfy<Jay. Ha ez nem így
van, akkor a program a két If utáni összetett utasításban az értékekeket
felcseréli. A cserét úgynevezett ciklikus cserével oldja meg.
A Puf változó
a valódiak ideiglenes tárolására lettek deklarálva. Vegyük figyelembe azt is, hogy a Bfx, Bfy
maximális értéke kisebb mint 80 illetve kisebb mint
Program Ablakok;
Uses NewDelay, Crt, CrtPlus;
Var Bfx,Bfy,Jax,Jay,Puf:
Byte;
I: Integer;
Sz:String;
Begin
TextMode(CO80);
Randomize;
Repeat
Inc(I);
Bfx:= Random(76)+1;
Bfy:= Random(22)+1;
Jax:= Random(78)+1;
Jay:= Random(23)+1;
If Bfx>Jax Then
Begin
Puf:= Bfx;
Bfx:= Jax;
Jax:= Puf;
End;
If Bfy>Jay Then
Begin
Puf:= Bfy;
Bfy:= Jay;
Jay:= Puf;
End;
Str(I,Sz);
Ablak(Random(8),Random(16),Bfx,Bfy,Jax,Jay,True,Sz);
Sound(100*Bfx);
Delay(150);
Until KeyPressed;
NoSound;
End.
Most is látunk
újdonságot. Az I típusa: Integer, azaz egész. Értéke -32768 –től +32767-ig változhat most azért, hogy lehetőségünk
legyen akár több száz, vagy ezer ablakot is a képernyőre rajzolni, illetve
eddig számolni. A következő az Inc eljárás, melyet I-re alkalmazva annyit tesz,
hogy értékét 1-el növeli, azaz számol. Az I változóban az ablak sorszáma lesz
tárolva, tehát ezt kell szövegként az ablak eljárásnak átadni, és itt az utolsó
újdonság, a számot String-é
kell alakítani. Ezt végzi az Str eljárás, melynek
első paramétere egy szám, második paramétere az a String, amelybe a számot String-ként írja,
itt Sz. A keltett hangot, az egyébként is véletlenül
létrejövő Bfx segítségével határoztuk meg. Ezzel a
feladatot maradéktalanul megoldottuk.