A Pascal nyelv
A
Turbo Pascal 7.0
egy magas szintű programozási nyelv. A nyelv használatához a saját gép, C:
meghajtó, Tp mappa, Bin
mappa, Tpx futtatható állománya által
létrehozható, DOS-os (karakteres képernyő, de egér van) Integrált Fejlesztői
Környezet (IDE) áll rendelkezésre.
A Tpx betöltése után a File
menü Change dir…
parancsával be kell állítani a felhasználó home-könyvtárához
rendelt hálózati meghajtót, illetve ezen belül - a már előzőleg létrehozott -
Pascal könyvtárat, amelyben a programlistákat és egyéb lemezes állományokat
tároljuk.
Megírt
programjainkat F2 billentyű segítségével menthetjük lemezre (home-könyvtárba), előzőleg kimentett állományokat F3
billentyű segítségével tölthetjük be.
A programlista
szerkesztését a Word szövegszerkesztőben megszokott módon végezhetjük,
válthatunk beszúrásos és felülírásos üzemmódok között az INS billentyű
segítségével, kijelölhetünk szöveget egérrel vagy billentyűzet SHIFT +
nyilak segítségével, CTRL+INS segítségével a kijelölt szöveget
lefényképezhetjük, SHIFT+INS segítségével a kurzor helyére beszúrhatjuk.
A programlistát F9
segítségével fordíthatjuk, CTRL+F9 segítségével futtathatjuk.
Fordításnál
előforduló gyakori hibaüzenetek:
";" expected (az idézojelbe tett
írásjel - ez esetben pontosvessző - hiányzik)
Unknow identifier (ismeretlen
azonosító, valamely kulcsszót helytelenül írtunk, vagy valamely változót még
nem deklaráltunk)
A
Pascal program szerkezete.
(program-fej:)
Program programnev;
Uses Crt, CrtPlus;
(deklarációs rész:)
Label 1;
Const max=100;
Var
i,j: byte; (globális változók)
Type rekord= Record
Mezo1: String;
Mezo2: Integer;
End;
Var
rek1: rekord;
Procedure proc1;
Var
a:byte; (lokális változók)
Procedure alproc1; (lokális eljárás)
Begin
(utasítások)
Edn;
Begin (proc1 végrehajtó része)
(utasítások)
End;
Function func1;
Begin
(utasítások)
func1:=valami;
End;
.
.
.
(főprogram, végrehajtó rész:)
Begin
(utasítások)
End.
A
program írásakor használandó lefoglalt szavak és magyarázatuk.
Program
: a programlista első szava, jelzi, hogy a lista
programot tartalmaz.
Begin : kezdet, mely a főprogram, az eljárások vagy
függvények, illetve összetett utasítások kezdetét jelzik.
End. : vége, a főprogram és
egyúttal a programlista végét jelzi, a fordító azokat a jeleket, amelyeket ez
után írunk, már nem értelmezi.
End; : vége,
eljárások, függvények, összetett utasítások, CASE szerkezet, RECORD
szerkezet végét jelzi.
Uses : használat, a program futtatásához szükséges UNIT-ok listáját e kulcsszó után kell -
vesszővel elválasztva - felsorolni.
Var :
változó, a programban használt változókat előre deklarálni kell, meg kell
mondani mi a neve és milyen típusú.
Const :
konstans deklarációja, név = érték formában.
String : a
szöveges változó típusneve.
Array : a tömb
típusú változó típusneve.
Label : címke,
a feltétel nélküli utasítás címkéje.
Of :
tömbelemek típusa, File elemeinek típusa, Case szerkezet része.
In :
halmazba tartozás művelete.
Mod : egész
osztás maradéka.
Div : egész
osztás hányadosa.
Absolute : az abszolút memóriacím megadása előtt alkalmazandó
lefoglalt szó.
With :
minősítő utasítás, rekord mezőire a rekord neve kiemelése után, rekordnév
nélkül hivatkozhatunk. Szintaktikája: With
(rekordnév) Do Begin
(mezonév1:= ... mezonév2:= ...)
End;
Procedure :
eljárás, helye a deklarációs részben van, szerkezete a program szerkezetével
megegyezik, a főprogramban, eljárásokban és függvényekben tetszőleges sokszor
hívható (használható).
Function :
függvény, gyakorlatilag olyan, mint az eljárás, csak visszaadott értékkel bír.
Hívható eljárásként is, de úgy is, hogy egy értékadó utasítás jobb oldalában
használjuk.
A
program végrehajtási sorrendjét meghatározó struktúrák.
Szekvencia: végrehajtási sorrend, ha a lista értelmezéséből
egyéb végrehajtási sorrend nem következik, akkor az utasításokat a gép a beírás
sorrendjében, azaz balról jobbra, fentről lefelé haladva, kihagyás nélkül
hajtja végre.
Iteráció: ismétlés, azaz a program egy részletét gép nem
egyszer, hanem többször, esetleg egyetlen egyszer sem hajtja végre. A Pascal
nyelvben három féle iteráció áll rendelkezésünkre.
1.
A For ciklus. Elől tesztelő léptető iteráció.
Szintaktikája: For i:=1 To
100 Do +az ismétlendő utasítás. Példánkban az i
egy sorszámozott típusú változó (ez esetben egész szám), neve ciklusváltozó,
melynek kezdoértéke 1, végértéke 100, és a
végrehajtásban értéke egyesével növekszik. Gyakorlatilag az a szerepe, hogy
számolja a végrehajtások számát. Természetesen az ismételendő utasításban az i-nek
szerepe lehet, értékét viszont átírni nem szabad. A Do
hatásköre egyetlen utasítás. Ha több utasításra szeretnék ezt kiterjeszteni,
akkor azokat egy összetett utasításba (Begin … End;-be) kell összefogni. A For
ciklust akkor kell használni, amikor előre tudjuk azt, hogy hányszor kell
ismételni a ciklus magját (az ismétlendő utasításokat). Ha a kezdő és
végértékek úgy jöttek létre, hogy a végérték a kisebb, akkor a For ciklus magja egyetlen egyszer sem hajtódik
végre. Ha nagyobb kezdő értéktől kisebb végértékig szeretnénk a
ciklusváltozóval haladni (szintén egyesével), akkor a To
helyett DownTo-t kell használni. Az így leírt
ciklus magja akkor nem fog egyetlen egyszer sem végrehajtódni, ha a kezdoérték kisebb, mint a végérték. A For
ciklus az egyik leggyakrabban használt programszerkezet.
2.
A Repeat ciklus. Hátul tesztelő ismétlő eljárás.
Szintaktikája: Repeat ... (utasítások) ... Until (logikai kifejezés); Az utasítások
mindaddig amig a logikai kifejezés igazzá nem válik,
ismétlődik. Mivel a logikai kifejezés vizsgálata az utasítások után van, ezért
az utasítások legalább egyszer végrehajtódnak. Ezt az ismétlő eljárást tehát
akkor használjuk, ha nem ismert előre, hogy az eljárásokat hányszor kell
végrehajtani, de legalább egyszer igen, és az ismétlés befejezéséről a program,
vagy a felhasználó (valamilyen beavatkozó szervvel: billentyuzet,
egér stb.) gondoskodik.
3.
A While ciklus. Elől tesztelő ismétlő eljárás.
Szintaktikája: While (logikai
kifejezés) Do (utasítás). Az
utasítás mindaddig végrehajtódik, ameddig a logikai kifejezés igaz értékkel
bír. Mivel a logikai kifejezés vizsgálata az utasítás előtt megtörténik, lehet,
hogy az utasítás egyetlen egyszer sem hajtódik végre, mert lehet, hogy a
logikai kifejezés már kezdetben False értékkel bír.
Mivel a Do csak egyetlen utasításra
vonatkozik, több ismételendő utasítás esetén, azokat egy összetett utasításba
kell zárni. Ezt az ismétlő eljárást akkor használjuk, ha nem ismert előre, hogy
az eljárásokat hányszor kell végrehajtani, sőt még az is lehetséges, hogy
egyetlen egyszer sem, ugyanakkor az ismétlés befejezéséről a program, vagy a
felhasználó (valamilyen beavatkozó szervvel: billentyűzet, egér stb.)
gondoskodik.
Szelekció: választás vagy elágazás, azaz a program egy
részletét a gép egyszer, vagy egyetlen egyszer sem hajtja végre. A végrehajtás
vagy logikai értéktől, vagy egy sorszámozott típusú (egész vagy karakter) változó
értékétől függ. Lehetőségek:
1.
Az If utasítás. Egyszerű elágazás. Szintaktikája: If (feltétel) Then
(utasítás) Else (utasítás). Azaz
utasítások végrehajtását egy feltételhez kötjük, vagyis, ha a feltétel igaz,
akkor a Then utáni, ha nem akkor az Else utáni utasítás hajtódik végre. Az Else előtt pontosvessző nem állhat. Az
egyszerű If szerkezetben az Else hiányzik, így csak egyetlen utásítás végrehajtása függ a feltétel logiakai
értékétől: ha igaz akkor végrehajtódik, ha nem igaz a
feltétel, akkor nem. Az utasítások helyén több utasítás nem, legfeljebb egy
összetett utasítás szerepelhet.
2.
A Case utasítás. Többszörös elágazás. Szintaktikája: Case (szelektor) Of
érték1: utasítás; érték2: utasítás; ...; Else utasítás; End;.
A szelektor értékétől függően (amely maga valamilyen sorszámozott típusú
változó) a felsorolás azon sora (csak egyetlen) hajtódik végre, amelyet a
szelektor aktuális értéke kijelöl. Az Else
ág használata nem kötelező, ekkor lehet, hogy olyan lesz a szelektor értéke,
amely a felsorolásban nem szerepel, ekkor egyetlen utasítást sem hajt végre a
szerkezet. Az Else előtt a
pontosvessző használata szükséges. Az utasítások helyén több utasítás nem,
legfeljebb egy összetett utasítás szerepelhet.
Feltétel
nélküli ugró utasítás :
a gép feltétel nélkül, egy meghatározott programsorra ugrik, illetve befejezi a
program végrehajtását. Lehetőségek:
1.
A GoTo. A programlista bármely helyét címkével
láthatjuk el. A GoTo + címke neve -
eljáráshívással a program feltétel nélkül a címkével jelölt hely utáni első utasítással
folytatódik. A címke után kettőspontot kell írni. A címke nem állhat olyan
kulcsszavak között, melynek szintaktikája összetett. Azaz pl. egy For ciklus kulcsszavai között. A GoTo nem illeszkedik a strukturált programozási
környezetbe, ezért használatát kerüljük.
2.
Az Exit. Eljárásból vagy függvényből feltétel
nélküli kiugrást előidéző eljárás. A gép a program végrehajtását az eljárás
vagy függvény hívási helye utáni utasításon folytatja.
3.
A Halt. A
program feltétel nélküli befejezését előidéző eljárás. A gép vezérlése
visszakerül a programot elindító környezetbe.
Változók
típusai.
A Pascal nyelvben
minden változót a használatba vétel előtt deklarálni kell. Érvényességi kör
szerint kétféle változót ismerünk, globálist és lokálist. A
globális változót a programlista bármely helyén használhatjuk. A lokálist,
melyet eljárásban vagy függvényben deklarálhatunk, csak az adott eljáráson vagy
függvényen belül.
Egész típusú
változók:
Byte: 0-255 egész szám.
Integer: -32768 - +32767 közötti egész szám.
Longint: kb. -2 milliár - +2 milliárd közötti egész szám.
Word: 0-65535 közötti egész szám.
Valós típusú
változók:
Real : valós.
Szöveges
típusú változók:
String: 0-255
hosszúságú szöveg.
Char: tetszőleges
karakter (pontosan 1 hosszúságú).
Logikai
változó:
Boolean :
logikai típus, értéke vagy True (igaz) vagy False (hamis).
Strukturált
változók:
Array: tömb, indexes
változók. Az indexek száma szerint nevezhetjük vektornak (ha egy), mátrixnak (ha
kettő) vagy többdimenziós tömbnek (ha kettőnél több az indexek száma). Nagy
előnye, hogy egy néven nagyon sok azonos típusú változót tudunk tárolni.
Record: rekord. Olyan
változó, amelyben egy adott dolog több különböző típusú jellemzőjét tudjuk
tárolni. Az egyes jellemzoknek a neve: mező. A mezők
tetszőleges előre deklarált típusok lehetnek. Ha ebből a típusból sok változónk
van, akkor használhatunk rekord-tömböt, amely az egyik legbonyolultabb
változótípus a Pascal nyelvben.
File típusú
változók:
File :
háromféle file-típus létezik.
Első a Text, amely szöveges állományt jelöl. A file-ba writeln-el lehet írni, readln-el lehet belőle olvasni (azaz teljes sorokat),
hiszen sorokra tagolt a #10#13 karakterekkel.
Második a tipizált,
melybe write-al írhatunk és read-al
olvashatunk, elemei előre definiált típusú változók, leggyakrabban rekordok.
Mivel minden eleme egyenlő hosszú, így az írás és olvasás pozicionálható.
Harmadik a típus
nélküli állomány, melynek írása és olvasásakor az értékek értelmezését a
programnak (a program írójának) kell végrehajtani. Az írás és olvasás vagy byte-onként, vagy blokkonként történik.
Fontosabb
eljárások és függvények.
Write : ir, paraméteres eljárás, a konstans paraméter értékét
változatlanul, a változót aktuális értékével jeleniti
meg. Megengedett több paraméter, melyeket vesszővel kell elválasztani. Ha az
első paraméter egy logikai file-név, akkor a logikai file-hoz hozzárendelt lemezes állományba ír. WriteLn alakban az írást követően egy soremelést is
végrehajt. Szöveges lemezes állomány irására csak WriteLn használható. Paraméter nélkül soremelést hajt
végre.
Read :olvas,
paraméteres eljárás, a változó paraméter értékét a beolvasott értékkel tölti.
Megengedett több paraméter, melyeket vesszővel kell elválasztani. Ha az első
paraméter egy logikai file-név, akkor az olvasás a
logikai file-hoz rendelt lemezes állományból
történik. ReadLn alakban az olvasás a #10#13 jelig
történik, azaz egy teljes sort olvas a szöveges állományból. Mivel a számitógép a billentyűzethez is egy szöveges állományt
rendel, igy a billentyűzetről is csak ReadLn-al olvashatunk. Paraméter nélkül lényegében a gép
arra vár, hogy a billentyűzeten az Enter billentyűt
megnyomjuk.
Readkey : billentyűzet-puffer olvasása, függvény, visszaadott
értéke a megnyomott billentyű kódja.
KeyPressed : függvény, logikai visszaadott értékkel, amely True, ha meg volt nyomva a billentyű (azaz a
billentyűzet-puffer nem üres), és False egyébként.
ClrScr :
képernyőtörlés, eljárás, mely az aktuális képernyőt az aktuális háttérszínnel
törli, és feltölti a képernyot #32-es karakterekkel,
azaz Space-szel.
GoToXY : kurzor
pozicionálása a képernyőn, paraméteres eljárás, két paraméterének a jelentése:
X: 1-80 (vízszintes paraméter, oszlopszám), Y: 1-25 (függőleges paraméter,
sorszám). Ha a paraméterek hibásan vannak megadva, akkor a program nem ad
hibajelzést, csak egyszerűen nem hajtja végre az eljárást.
TextBackGround : szöveg háttérszín, paraméteres eljárás, beállítja a
szöveg háttár színét, csak akkor látjuk hatását, ha a beállítást követően
írunk, vagy képernyőt törlünk. Megengedett paraméter értékek: 0-7.
TextColor : szövegszín, paraméteres eljárás, beállítja a szöveg
színét, csak akkor látjuk hatását, ha a beállítást követően írunk a képernyőre.
Megengedett paraméter értékek: 0-15.
Window : ablak,
paraméteres eljárás, az aktuális ablakméretet állítja a képernyőn. Négy
paramétere: bal felső csúcs X koordinátája, bal felső csúcs Y koordinátája,
jobb alsó csúcs X koordinátája és a jobb alsó csúcs Y koordinátája. Az X
koordináták (1. és 3.) értéke 1-80 között, az Y koordináták értéke (2. és 4.)
1-25 között kell lenni, valamint az első koordinátának kisebb-egyenlőnek kell
lennie a harmadiknál, a másodiknak pedig a negyediknél. Ha a paraméterek
hibásan vannak megadva, akkor a program nem ad hibajelzést, csak egyszerűen nem
hajtja végre az eljárást.
Chr :
karakter előállítása számból, paraméteres függvény, a paraméter értéke 0-
Random :
véletlen szám előállítása, paraméteres függvény, egész "n" esetén
véletlen számot kapunk a [0, n-1] intervallumból. Alkalmazása: először
megállapítjuk, hány véletlen számból választunk, majd megállapítjuk, hogy
melyik az első szám, amit kapni szeretnénk, és ezt a zárójelen kívül még
hozzáadjuk a kapott véletlen értékhez. Ha például [100, 200] intervallumból
kellenek véletlen számok, akkor ez: random(101)+100.
Randomize : a véletlen-szám generátor inicializálása, annak
érdekében, hogy a véletlen számsor, ne mindig ugyanaz legyen, paraméter nélküli
eljárás.
Delay :
várakozás, paraméteres eljárás, a paraméter a várakozás időtartama ezred
másodpercben.
Sound : hang,
paraméteres eljárás, a paraméter a megszólalandó hang frekvenciája.
NoSound : hanggenerátor kikapcsolása, paraméter nélküli
eljárás. Ha a hanggenerátort nem kapcsoljuk ki, akkor az a program befejezése
után is szól.
Str :
számnak stringgé történő átalakítása, kétparaméteres
eljárás, első paraméter a szám, második a string,
amivé alakítjuk a számot, a második paraméter csak változó lehet.
Val : string-nek számmá történő átalakítása, háromparaméteres
eljárás, első paraméter az átalakítandó string, a
második az az egész változó, amelybe a szám kerül,
harmadik paramétere egy integer tipusú változó,
amelyből kiolvasható, hogy milyen sikeres az átalakítás, ugyanis az első át nem
alakítható karakter helyét jelzi, ha tehát ez nulla, akkor az átalakítás
sikeres.
Lo : alsó
byte, paraméteres függvény, a word tipusú paraméter alsó byte-ja a visszaadott érték.
Hi : felső
byte, paraméteres függvény, a word tipusú paraméter felso byte-ja a
visszaadott érték.
Sqrt :
négyzetgyök, paraméteres függvény, a visszaadott érték a paraméter
négyzetgyöke.
Sin : sinus, paraméteres függvény, a visszaadott érték a
paraméter sinusa, a paraméter a szögérték radiánban kifejezve.
Cos : cosinus, paraméteres függvény, a visszaadott érték a
paraméter cosinusa, a paraméter a szögérték radiánban
kifejezve.
Pi : pi, a nevezetes szám-konstans, 3.141569…
Length : szöveghossz, paraméteres függvény, a paraméternek
mint szövegnek a hossza.
Copy : szövegmásolás,
háromparaméteres string-értéku függvény, az első
paraméter a forrás string, a második egy szám, mely a
másolt string első karakterének helye a forrásban, a
harmadik paraméter a másolt karakterek száma.
Ord :
sorszám, paraméteres függvény, a paraméter valamely sorszámozott típusú érték,
a visszaadott érték a sorszámozott típusú változó sorszáma.
Insert :
szövegbeszúrás, háromparaméteres eljárás, az elso
paraméter a beszúrandó szöveg, második az a string,
amelybe beszúrunk, harmadik paraméter azt adja meg, hogy hányadik helytől
kezdve szúrunk be.
Delete :
szövegrész törlése, háromparaméteres eljárás, az első paraméter a csonkitandó szöveg, második az első törlendő karakter
helye, harmadik a törlendő karakterek száma.
Pos
: szövegrész keresése, kétparaméteres függvény, az első az a string amit keresnünk, a második
az a string amiben keresünk. A visszaadott érték az a
hely, ahonnan kezdve az első string megtalálható a
másodikban, ha nem található meg, akkor a visszaadott érték 0.
UpCase :
nagybetűs alak, paraméteres függvény, a paraméter egy karakter, a visszaadott
szintén, csak mindenképp nagybetűs alakot ad, ha az adott karakternek olyan
alakja létezik.
FillChar : karakterekkel való feltöltés, háromparaméteres
eljárás, az első egy változó, második egy szám, mely megmutatja, hogy a változó
hány byte-ját töltjük fel a harmadik paraméterként megadott karakterrel. Ez
lényegében közvetlenül a memóriába ír, a paraméterként átadott memóriahelyére.
SizeOf :
változó mérete, paraméteres függvény, a paraméter valamely változó, visszaadott
értéke a változó mérete byte-okban.
Inc : növelés, kétparaméteres eljárás, első paraméter az az egész tipusú változó, amelyet
növelni szeretnénk, a második paraméter azt mutatja, hogy mennyivel. Egy
paraméterrel is hivható, ekkor a növelés eggyel
történik.
Dec :
csökkentés, kétparaméteres eljárás, első paraméter az az
egész tipusú változó, amelyet csökkenteni szeretnénk,
a második paraméter azt mutatja, hogy mennyivel. Egy paraméterrel is hivható, ekkor a csökkentés eggyel történik.
Round :
kerekítés, paraméteres függvény, paramétere valós, visszaadott értéke egész
típusú, a paraméter egészre való kerekítése.
Unitok
Standard
Unitok. Az IDE által generált *.EXE állományok nagysága két dologtól függ. Egyrészt attól,
hogy milyen hosszú programlistát írunk, másrészt attól, hogy mennyire
szerteágazó témakörökből tartalmaz eljárásokat, függvényeket (pl.:
képernyőkezelés, lemezkezelés, grafika …). Annak
érdekében, hogy a futtatható állomány mérete csak a szükséges méretű legyen, a
különböző részterületekhez kapcsolódó eljárásokat és függvényeket egy-egy egységbe
(UNIT-ba) helyezték el, melyet a program elején
használatba kell venni (Uses kulcsszó). A következő
standard unitokat fogjuk használni:
System: az alapvető eljárásokat és függvényeket tartalmazó unit,
automatikusan használatba kerül, minden futtatható állományba beszerkesztődik.
Crt:
a képernyő és billentyűzet kezelésével kapcsolatos eljárások és függvények.
Dos:
dátummal, idővel, lemezkezeléssel kapcsolatos eljárások és függvények.
Printer: nyomtató használatához.
Graph: a pascal két alapvetően különböző képernyőkezelést ismer. Az egyik a
karakteres, a másik a grafikus. Alapértelmezésben a képernyő karakteres. A
grafikus megjelenítéshez különböző interfész állományokat használhatunk, melyek
kiterjesztése *.BGI (Borland
Grafikus Interfész). Ezek használata a Graph unit
segítségével lehetséges.
A Unit szerkezete:
Unit unitnev;
Interface
Uses unit1,unit2…;
Procedure proc1;
Procedure proc2;
…
Function func1;
...
Const (globális)
Type (globális)
Var (globális)
Implementation
Procedure proc1;
Begin
End;
Procedure proc2;
Begin
End;
…
Function func1;
Begin
End;
…
End.
A CrtPlus
A Pascal
fejlesztői megadták a lehetőséget a felhasználóknak is arra, hogy saját UNIT-okat szerkesszenek. Ennek az a hatalmas előnye, hogy
egyszer jól megírt eljárásainkat UNIT-ban tárolva
nagyon sokszor és sokáig használhatjuk. Mi a Crt-hez
kapcsolódó eljárásainkat és függvényeinket a CrtPlus-ban
helyeztük el.
KeyEmpty : eljárás, billentyűzet-puffer ürítő.
Varj :
eljárás, vár addig, ameddig meg nem nyomunk egy billentyűt. A billentyűzet
puffer üres lesz az eljárás után.
Felre : eljárás, kurzor az aktív képernyő bal alsó sarkába
ugrik
Tunj :
eljárás, a kurzor eltűnik
Szinek :
kétparaméteres eljárás, első paraméter a háttérszín, második a karakterszín
Szinez :
ötparaméteres eljárás, a képernyő egy sorában megadható darabszámú képernyőhely
színeit állítja be. Paraméterek:
1.
hsz: háttérszín;
2.
ksz: karakterszín;
3.
x koordináta;
4.
y koordináta;
5.
karakterek száma.
Torol : eljárás, a képernyő egy adott helyétől törli a
képernyő memórát
Tolt : eljárás, a képernyő egy adott helyétől adott
karakterrel tölti a képernyő memóriát
WriteXY : háromparaméteres eljárás, képernyő adott helyére ír.
Paraméterek:
1.
x koordináta;
2.
y koordináta;
3.
kiírandó szöveg.
IrXY :
eljárás, adott helytől, adott stringet ír a képernyő
memóriába
Vvonal :
háromparaméteres eljárás, a képernyő adott sorába vízszintes vonalat húz.
Paraméterek:
1.
x kezdőhely;
2.
x véghely;
3.
y sor.
Fvonal :
háromparaméteres eljárás, a képernyő adott oszlopába függőleges vonalat húz.
Paraméterek:
1.
x oszlop;
2.
y kezdőhely;
3.
y véghely.
Keret : négyparaméteres eljárás, a képernyőre egy téglalap
alakú keretet rajzol. A négy paraméter a Window
standard eljárás paramétereivel teljesen megegyezik. (bfx:
bal felső csúcs x koordináta, bfy: bal felső csúcs y
koordináta, jax: jobb alsó csúcs x koordináta, jay: jobb alsó csúcs y koordináta)
Racs :
hatparaméteres eljárás, a képernyőre egy téglalap elrendezésű rácsot helyez.
Paraméterek:
1.
bfx;
2.
bfy;
3.
bx: a rács egy cellája belsejének x irányú mérete;
4.
by: a rács egy cellája belsejének y irányú mérete;
5.
nx: cellák száma vízszintesen;
6.
ny: cellák száma függőlegesen.
Ablak : nyolcparaméteres eljárás, a képernyőre egy színes,
keretes, címkés, árnyékos ablakot rajzol. Paraméterek:
1.
hsz;
2.
ksz;
3.
bfx;
4.
bfy;
5.
jax;
6.
jay;
7.
arny: logikai paraméter, ha igaz, akkor van árnyék, ha hamis akkor nincs;
8.
c: az ablak
címkéje.
Uzenet :
háromparaméteres eljárás, a képernyő közepén egy ablakban egy egysoros üzenetet
jelenít meg. Paraméterek:
1.
hsz;
2.
ksz;
3.
sz: a megjelenítendo szöveg,
az ablak bármely billentyűre eltűnik.
Kerdezo : háromparaméteres függvény, mely egy eldöntendo kérdést tesz fel, visszatérési értéke logikai.
Paraméterek:
1.
hsz;
2.
ksz;
3.
sz: a kérdés, csak igen vagy nem válaszra lép ki a
függvényből.
Bevitel : ötparaméteres függvény, amely a képernyő egy helyétől
kezdve, legfeljebb a képernyősor végéig egy szerkeszthető beviteli területet
ad. A visszaadott érték a bevitt szöveg. Paraméterek:
1.
hsz;
2.
ksz;
3.
x;
4.
y;
5.
sh: a szerkeszthető karakterek száma.
Password : függvény, mely a Bevitel függvénnyel bevitt stringről megállapítja, hogy mint jelszó, helyes-e
Menu : nyolcparaméteres
függvény, mely a képernyőn megjelenített menüpontokból a vezérlőbillentyűk
segítségével választási lehetőséget biztosít. Visszaadott értéke a kiválasztott
menüpont sorszáma. Paraméterek:
1.
hsz;
2.
ksz;
3.
vsz: választósor háttérszine;
4.
bfx;
5.
bfy;
6.
sh: menüsorok hossza;
7.
ss: menüsorok száma;
8.
as: aktuális menüsorszám, egyúttal a visszaadott érték
is.
ValidSt : egyparaméteres függvény, mely a paraméterként kapott stringbol eltávolítja a fölösleges Space-szeket
és így adja vissza.
Listazo : tízenegy-paraméteres
függvény, mely a képernyőn egy string-listát jelenít
meg, melyből a vezérlőbillentyuk segítségével
választhatunk, visszaadott értéke a kiválasztott string
indexe. Paraméterek:
1.
hsz;
2.
ksz;
3.
vsz: választósor háttérszine;
4.
bfx;
5.
bfy;
6.
sh: string-ek hossza;
7.
ss: string-ek száma;
8.
ls: egyszerre látható string-ek
száma (az ablak magassága);
9.
as: aktuális string-index, egyuttal a visszaadott érték;
10. arny: logikai érték, a megjelenítő ablaknak legyen-e
árnyéka;
11. c: a lista címkéje.
Listara : egyparaméteres függvény, nemlátható
elem, lényegében egy rendezett string-listát kezel,
mely a globális string-tömbben található, visszaadott
értéke a string-lista elemszáma, nem enged meg azonos
string-eket a listában, a listára helyezés előtt a string-eket validálja (ValidSt).
Listarol : egyparaméteres függvény, nemlátható
elem, lényegében egy rendezett string-listát kezel,
arról törli a paraméter stringet, mely a globális string-tömbben található, visszaadott értéke a string-lista elemszáma, ha nem volt a listán, akkor 0-t
kapunk. (a paraméter stringet validálja
(ValidSt)).
FileKereso : függvény, az aktuális könyvtár, adott kiterjesztésű
állományait a Listazo függvény segítségével
megmutatja, visszaadott értéke a kiválasztott file
neve.
Gomb : eljárás, mely egy feliratos, egérrel kezelhető
nyomógomb látványát adja.
GombKereso : függvény, mely megadja az adott helyen található
egérrel kezelhető látvány Hot-Key kódját.
ImputLine : függvény, mely címkés beviteli sort kezel,
visszaadott értéke a bevitt String.
RadioGomb : eljárás, mely egy egérrel választható, rádiógomb
kapcsoló látványát állítja elő.
RadioGombKapcs : függvény, mely rádiógomb választását adja vissza.
Destop :
eljárás, mely a Turbo Pascal IDE látványát adja,
egérrel kezelhető felületek alapja, Alt-X -re kilép.
Binaris : függvény, mely az átvett Word típusú számnak a kettes
számrendszerbeli alakját adja vissza String-ként.
Tizes :
függvény, mely az átvett String-ből a szám tízes
számrendszerbeli alakját adja vissza.
SetBit :
függvény, mely az átvett Word típusú számnak a megadott sorszámú bitjét az
adott bitre állítja.
ValtBit : függvény, mely az átvett Word típusú számnak a
megadott sorszámú
OOP
(Objektum Orientált Programozás Pascal
nyelven)
1. Programok szerkezete
1.1 Lineáris szerkezet
A lineáris
programfelépítés a hagyományos, a programozás kezdetén alkalmazott szerkezet.
Általában rövid programokat írunk vagy írtunk ilyen felépítésben. A program
alapjában véve lineáris lefutású, az első utasítástól az utolsóig történik a
végrehajtás. A program futását esetleg egy inputra való várakozás szakíthatja
meg, vagy a végén, a kimeneti képernyő megtartására írunk végtelen ciklust. Ezt
a programozási stílust figyelhetjük meg a hagyományos BASIC programoknál.
De már a
BASIC is megadta a lehetőséget a továbblépésre azzal, hogy GOSUB - RETURN
szerkezeteket engedett meg. Ennek a lehetőségnek a kihasználásához
elengedhetetlen a GOTO utasítás, mely idegen a strukturált programozástól. A
programozó tanulása során általában kisebb lélegzetű programoktól halad a
nagyobbak felé, megtanulva azt, hogyan kerülheti el azt a softver
krízist, amikor a saját programját már csak nehezen látja át, és csak nagy
nehézségek árán tudja továbbfejleszteni. Ezt segítheti BASIC-ben
a változótábla. Kezdetben maximum csak két karakteres lehetett a változónév,
ezért külön tervet kellet készíteni, hogy milyen néven milyen változókat
használunk a programban. A másik a programlista felosztása: a lista elejére
helyezte a szubrutinokat, a végére a főprogramot, amely esetleg már menüt is
tartalmazott. Ez a felépítés már hordozott magában némi strukturáltságot, de a
lista alapvetően még mindig lineáris, hiszen a szubrutinoknak nincsenek
határozott belépési pontjai, így semmilyen zártságot nem mutat. Maga a BASIC
ezt nem támogatja.
A BASIC, és
ezáltal a lineáris programszerkezet legnagyobb hibája az, hogy nem ad igazi
lehetőséget a kód-újrafelhasználásra, amely mint az a továbbiakban látható, a
legfontosabb eszköze a hatékony programozási gyakorlatnak. A fejlődés és
fejlesztés legfőbb mozgatója pedig az, hogy hogyan lehet minél hamarabb minél
hatékonyabb és biztonságosabb programot írni. Ehhez a kód-újrafelhasználás
megoldása elengedhetetlen.
1.2 Moduláris szerkezet
A moduláris programozásban a program
által megoldandó feladatot részekre, úgynevezett modulokra bontjuk szét.
Megírjuk a programvázat, amelyben egy-egy modul egy-egy részfeladat
megoldásáért felelős. Készíthetünk egy főmodult is, amely a modulokat egységbe
foglalva kezeli. A PASCAL nyelv támogatja a moduláris programozást. A modul
ebben a nyelvben lehet eljárás vagy függvény. A két modul nagyon hasonló,
lényegében csak abban különbözik, hogy a függvény visszaadott értékkel bír, és
így állhat valamely kifejezésben, vagy az értékadás jobb oldalán. Mindkét
modulnak létezhetnek paraméterei, mely a modul végrehajtást pontosítják, vagy
egyáltalán lehetővé teszik. A függvény visszaadott értékének típusa csak a
következő lehet: Boolean, Char,
String, Byte, Word, ShortInt,
Integer, Longint, Real, Double,
Extend és Pointer. Nem lehet viszont
semmilyen összetett, az előzőekből leszármaztatott vagy felhasználói típus. A
PASCAL lényegében eljárás orientált nyelv. Ügyesen megírt moduljainkat pedig
egy másik programban is használhatjuk, egyszerűen csak egységekbe (Unit) kell
elhelyezni őket, és a másik programban az egységet használatba kell venni. Ez
már igazi kód-újrafelhasználás. A moduláris programozás egyik módszere a
fentről lefelé történő kidolgozás, amikor megírjuk az úgynevezett főprogramot,
és a modulok fejeit pedig megfelelő számú egységben. Ha ügyesen szerkesztjük
meg az egészet, akkor a program már az első pillanatoktól futtatható. A modulok
kidolgozása csak ezek után következik úgy, hogy mindig csak egy-egy részt
finomítva haladunk a feladat teljes megoldásáig. A moduláris programozást
gyakran módszeres programozásnak is szokták nevezni. A moduláris programban a
modulok közötti kapcsolatot a változók biztosítják. Ezért a moduláris
programozás egy jól átgondolt adatstruktúrát feltételez, de erre még a
továbbiakban visszatérünk. A Pascal nyelv nem korlátozza az egy program által
használatba vehető egységek (Unit-ok) számát. Ennek gyakorlatilag a gép
memóriája szab határt. Ha ezt a határt is át szeretnénk lépni, akkor lehetőség
van az úgynevezett Overlay technika alkalmazására. Ez
gyakorlatilag azt teszi lehetővé, hogy ne minden Unit kódját kelljen egyszerre
a memóriába tölteni, hanem csak azokat, amelyeket a program éppen használ. Ha
később másikra van szüksége, akkor azt tölti be a használat idejére. Ezt az Overlay menedzser úgy hajtja végre, hogy először a
legnagyobb terjedelmű Unit-oknak foglal helyet, hogy a később ráírandó
legfeljebb csak egy Unit területét írja fölül. Ezzel a memória töredezettségét
minimálisra szoríthatjuk.
1.3 Menüvezérelt program
A menü a számítógépes programban
gyakorlatilag az interfész terület a program és a használója között. A menü
minden menüpontja mögött lényegében egy-egy funkció van, melyet a menü
kiválasztásával aktiválhatunk. Olyan ez mintha parancsokat adnánk a programnak,
hogy most kérem ezt végrehajtani, most pedig azt, vagy egyáltalán - legyen vége
a program futásának. Amikor moduláris programot használunk, akkor gyakorlatilag
a főprogram egy menü futtatását jelenti, melynek menüpontjai mögött egy-egy
kisebb nagyobb modul vagy egység van elhelyezve, megírva. A lineáris programfelépítéssel
szemben nyilvánvaló az előny: egy-egy programrészletet tetszőlegesen sokszor
újra meg újra lejátszhatunk anélkül, hogy kilépnénk a programból a futtató
környezetbe. Természetesen a legtöbb esetben ennek csak akkor van értelme, ha
közben a meghívott modul végrehajtását paraméterekkel megváltoztattuk. Ez nem
más, mint a programon belüli kód-újrafelhasználás.
1.4 Eseményvezérelt program
Miközben egy számítógépes programot futtatunk, számos
hatás érheti a programunkat. De természetesen csak akkor, ha eme hatásokat a
program képes felfogni, és arra ésszerűen reagálni. Ezeket
a hatásokat kiválthatja a felhasználó, vagy a számítógép bármelyik egysége,
erőforrása. A felhasználó leginkább a billentyűzethez vagy az egérhez nyúl a
program használata közben. Esemény lehet a billentyűzeten egy gombnak a
megnyomása, az egér megmozdítása illetve kattintás valamelyik egérgombbal,
esetleg ez utóbbi kettő egyidejűleg. De eseményeket generálhatnak a számítógép
egységei: a nyomtató üzen, hogy milyen az állapota (Pl.: kifogyott a papír), a
lemezes egység, hogy nincs kész az olvasásra, vagy írásvédett a lemez, vagy
elfogyott a memória valamely része. Ha egy program eseményvezérelt, akkor az
mindezen eseményekre valahogy reagálni képes. Az eseményvezérelt programban
általában objektumok vannak, amelyek kvázi független életet élnek, a közös csak
annyi bennük, hogy képesek reagálni az őket ért ingerekre, eseményekre és
képesek egymással kommunikálni. Egy eseményvezérelt program futása közben nyoma
sincs a lineáris végrehajtásnak. A gyakorlatilag véletlenül bekövetkező
események vezérlik a programot, nem lehet tehát azt tudni, hogy éppen melyik
objektum lesz aktív a következő pillanatban.
Egy eseményvezérelt program rendelkezik a következő
képességekkel:
-
események
begyűjtése egy eseménylistába;
-
események
szétszórása az objektumok felé;
-
az objektumok
kezelik az eseményeket, és ha nekik szólt, akkor törlik az eseménylistából;
-
ha egy eseményt
egyetlen objektum sem tudta lekezelni, akkor a főprogramnak kell rá reagálni
vagy hibaüzenettel, vagy figyelmeztetéssel a felhasználó felé, vagy csak
egyszerűen neki kell törölni az eseménylistából; és a legfontosabb képesség:
-
a program a sok
és véletlenül létrejövő esemény hatása ellenére - sőt talán éppen általuk –
működőképes tud maradni.
A napjainkban megírt bármely eseményvezérelt program
szinte biztosan egyúttal objektum orientált is. Nem meglepő tehát, ha már most
eláruljuk, hogy az igazi kód-újrafelhasználás az objektumorientált
programírással valósítható meg. De ahhoz, hogy ezt megértsük, még sok mindent
meg kell tanulnunk. Nézzük tehát, hogyan vezet az út a byte-októl az
objektumokig.
2. Adatstruktúrák
2.1 Adatok típusai
Nem tipizált nyelvek esetén a
programban bárhol használatba vehetünk egy változót. Méghozzá úgy, hogy annak
típusát, sőt még létezését sem írtuk le azt megelőzően. A program a változó
használatából dönti el azt, hogy milyen típusú. Ha számmal töltöttük fel, akkor
olyan típusúnak, ha szöveggel, akkor string-nek
(karakterláncnak) gondolja. Nem tipizált nyelvek esetében általában csak
egyféle összetett típus létezik, a tömb. Ennek alapértelmezett indexhatára
legtöbbször 0-10 között van. Ha ennél nagyobb tömbre van szükségünk, akkor ezt
előzőleg Dim prefixum segítségével dimenzionálni
kell. Az újradimenzionálást általában a nyelvek nem engedik meg. A tömb viszont
lehet számok és string-ek tömbje is, természetesen
egyszerre csak az egyik. Ilyen nem tipizált nyelv például a hagyományos Basic,
vagy az Excel táblázatkezelőben megtalálható Visual
Basic is.
Tipizált nyelvek esetén, mint
amilyen a Pascal nyelv is, csak előzőleg, azonosítójával és típusával megadott
(deklarált) változót vagy konstanst használhatunk programjainkban. Mivel ez
lényegében kötöttséget jelent, ezért a nyelv kidolgozói maximálisan igyekeztek
- a felhasználók igényeit figyelembe – minél több féle
adattípust beépíteni a nyelvbe, sokkal többet, mint az a nem tipizált
nyelveknél megfigyelhetünk. A Pascal nyelv adattípusainak egyik lehetséges
csoportosítása a következő:
-
egyszerű
-
sorszámozott
-
egész (byte, word, shortint, integer, longint)
-
logikai
-
karakter
-
felsorolt
-
intervallum
-
valós (real, double, extend)
-
karakterlánc
-
strukturált
-
tömb
-
rekord
-
objektum
-
halmaz
-
állomány (szöveges, nem tipizált, tipizált)
-
mutató
-
típusos
-
típus nélküli
-
eljárás
-
objektum.
Látva a
felsorolást megállapíthatjuk, hogy ez igen bőséges kínálatot jelent. Mindezek
az adattípusok lehetnek változók, konstansok vagy tipizált konstansok típusai.
Élettartamuk szerint
megkülönböztethetünk statikus és dinamikus változókat. A statikus változó a
program futásának minden pillanatában létezik és elérhető, helyfoglalása az
adatszegmensben történik, mely szegmens maximális mérete 64 kb lehet. (A konstansok helye a kódszegmens és az
adatszegmens között van.) A dinamikus változók a
program futása alatt jönnek létre és szűnnek meg, helyfoglalása a Heap-ben történik, melynek mérete az adatszegmens
többszöröse is lehet. Dinamikus változók használatánál gondoskodni kell a már
nem használandó változók megszüntetéséről és így az általa lefoglalt
memóriaterület felszabadításáról.
A változók érvényességi köre szerint megkülönböztetünk globális és lokális változót. A globális
változó a program bármely részéből látható és használható, értéke minden
pillanatban jól meghatározott, az utolsó rajta végzett műveletek által. A
lokális változókat modulokon belül hozzuk létre és érvényessége is csak az adott
modulra terjed ki, csak abban használható. Másik
jellemzője a lokális változóknak, hogy értéke a modul elhagyásával nem őrződik
meg, tartalma véletlenszerű érték lesz, újra visszatérve a modulba tehát újra
inicializálni kell. Ha egy modul által meghatározott értéket más modulokban,
vagy a főprogramban használni szeretnénk, akkor függvényt kell írnunk. Ez, mint
már említettük visszaadott értékkel bír. De azt is említettük, hogy csak elemi
típus visszaadására alkalmas. Ezen a problémán segít a változó paraméter típus
alkalmazása. Ha egy modult változó paraméterrel hívunk meg, amely természetesen
tetszőleges típusú lehet, és ha a modul gondoskodik arról, hogy a változó
paraméter értékét megfelelően beállítsa, akkor a meghívás után a változó
paraméter pozíciójában található változóban a
visszaadott értéket megtaláljuk. Így a visszaadott érték két lépésben
függvényhez hasonlóan elérhető.
Moduláris programozásnál a
program feladatának legjobban megfelelő adatszerkezet megalkotása a
legfontosabb programozói feladat. Egy ügyesen megszerkesztett struktúra már fél
siker a helyes program megírásban. Ezért az adatszerkezet megtervezésénél nagy
gonddal kell eljárni. Főleg akkor, ha nagy adatmennyiséggel dolgozik egy
program, mert ha utóbb kiderül, hogy valamely részadat utólagos elhelyezésére
nincs lehetőség, akkor a teljes adatállományt újra kell szervezni, a rossz
adatszerkezetű adatokat be kell olvasni a háttértárolóról, majd az új szerkezet
szerint ki kell írni azokat. Különben a program az új feladatokra nem lenne
alkalmazható.
2.2 Összetett adatszerkezetek
A felhasználó,
a nyelv adta lehetőségeket kihasználva adatait további struktúrákba
szervezheti. A lentebb említett adatszerkezetek mindegyike dinamikusan
létrehozott és valamilyen szisztéma szerint egymáshoz mutatókkal kapcsolódó
adatokat (rekordokat vagy objektumokat) jelentenek. A legfontosabbak
adatszerkezetek a következők:
Multilisták.
A multilisták olyan listák, amelyek elemeinek többféle rákövetője
és többféle megelőző eleme lehet. (Természetesen a mutatók által). Ilyen
például a menürendszer is.
Fák. A fák olyan multilisták, amelyek nem tartalmaznak kört, azaz nincs a
bennük lévő mutatóknak olyan sorozata, amelynek végigjárásával a kiindulási
elemhez visszajutnánk. Ilyen például a lemez file-szerkezete.
Bináris fák. Olyan
fák, amelynek minden elemében pontosan kettő rákövető található. Rendezésre
kiválóan alkalmas (jobbra nagyobb elemek, balra kisebbek).
Hálók. Olyan multilisták,
amelyben körök is találhatók.
Verem. A verem egy
speciális kezelésű lista. A veremnek általában van megengedett maximális
mérete, van kezdete és van vége. A verembe adatokat tölthetünk, és kivehetünk
onnan. Van egy mutató, amely a verem aktuális üres helyére mutat. Ha a
veremmutató a verem alján (elején) van, akkor üres, adat nem olvasható ki
belőle. Ha a mutató a verem tetején (végén) van, akkor a verem tele van, adatot
már nem tölthetünk bele. A veremből a legutoljára beirt adatot
olvashatjuk ki. A verem mérete általában nem változik. A verem adatait nem
szoktuk törölni, hanem betöltéskor fölülíródnak.
Puffer. A puffer egy speciális
kezelésű zárt lista. Adatok feldolgozás előtti ideiglenes tárolására alkalmas.
A Pufferbe adatokat írunk és veszünk ki belőle. A legelőször bekerült adatot
vehetjük ki legelőször (amely legrégebben vár a feldolgozásra, ezáltal az adat
keletkezésének sorrendjében történik a kiolvasás). A Puffer mérete általában
rögzített. Két mutatóval rendelkezik. Az egyik a kiolvasható elemre mutat, a
másik az első beírható helyre. A mutatók mindig a lista vége felé mozognak,
csak a végéről (a zártság miatt) visszalépnek az elejére. Ha a két mutató
ugyanarra az elemre mutat, akkor a puffer üres – nincs benne adat. Ha betöltési
helyet mutató pointer a kiolvasható elem előtti elemre mutat, akkor a puffer
tele van. A pufferben az adatok nem törlődnek, hanem betöltéskor fölülíródnak.
Minden perifériának van puffere a számítógépben. (Billentyűzet puffer, videó
puffer, lemez puffer, sőt még az operatív memóriának is van: a Chache memória, vagy gyorsító tár.)
2.3 Adatstruktúrákon
végezhető műveletek (karbantartás)
Az adatstruktúrákon
különböző karbantartási műveleteket kell végezni. Ezek lehetnek a
létrehozással, használattal valamint a megszüntetéssel kapcsolatos
tevékenységek. Nézzük meg ezeket a tevékenységeket egy
kicsi részletesebben.
Létrehozás. A különböző listák és fák dinamikusak méretük tekintetében is, azaz
kezdetben üresek, majd adatokkal töltjük fel őket. Ezeket
az adatszerkezeteket először létre kell hozni. Természetesen a deklarációs
részben le kell írni őket, majd futás közben New eljárással történik a
létrehozás. Amikor egy új elemet akarunk a listára tenni, akkor is a New-t kell
használni. Ha a listát már nem használjuk, akkor Dispose
eljárással elemit megszüntetjük, és így az általuk lefoglalt memóriát
felszabadítjuk.
Feltöltés, értékadás. Az
adatszerkezethez tartozó elemek adattároló mezőinek az általunk tárolni kívánt
adatokkal való fölülírása. Kezdetben inicializáló értékekkel, majd a
felhasználás során az aktuális értékekkel. A feltöltés lényegében egy értékadó
művelet, mely az inicializáló értékeket részben vagy teljesen fölülírja.
Beszúrás.
Beszúráskor a lista vagy a fa elemeinek száma növekszik. A beszúrás helye
szerint különböző lehetőségek vannak. Ha a lista nem rendezett, akkor általában
a lista végére szúrunk be. Ha rendezett, akkor a rendezési kulcs szerinti
helyre történik a beszúrás úgy, hogy a kérdéses helyen a láncot szétszakítjuk,
és a mutatókat az új elemre állítjuk, illetve az új elem mutatóit a lista
megfelelő elemeire irányítjuk.
Törlés. Törléskor a lista vagy a
fa elemeinek száma csökken. A törlés helyén a struktúrában maradó elemek
mutatóit egymásra kell irányítani.
Keresés. A keresés célja a
struktúra valamely elemének a megkeresés tulajdonsága vagy az adatszerkezetben
betöltött szerepe alapján. Ennek érdekében a struktúrát, vagy annak egy részét
be kell járni. A keresés eredménye általában a megfelelő elemére mutató
Pointer, vagy az elem megfelelő mezője.
Bejárás. Bejárás alatt általában
teljes bejárást értünk, azaz olyan lépegetés a struktúra elemein, aminek
eredményeképpen szisztematikusan, minden elemet csak egyszer érintve,
végigjárjuk az adatszerkezetet. Ezt általában akkor kell megtennünk, ha
valamilyen eljárást a struktúra minden elemére végre kell hajtanunk, vagy
egyszerűen csak meg szeretnénk számolni az elemeit.
Csere, rendezés.
Ha az adatszerkezet nem rendezett, akkor általában mindegy, hogy egy konkrét
elem hol helyezkedik el a struktúrában. Ha egy rendezetlen adatszerkezetet
rendezetté szeretnénk tenni, akkor a nem megfelelő sorrendben lévő elemeit fel
kell cserélni. Ez többféleképpen lehetséges. Egyik az elem pár teljes fizikai
cseréje: mindkettőt leválasztjuk, és a megfelelő helyre beszúrjuk őket. A másik
lehetőség a tartalom cseréje, azaz a megfelelő mezők értékeit cseréljük ki.
Ebben az esetben a speciális helyzetű (első, utolsó, elágazási ponton lévő)
elemek szerepét újra kell gondolni. Harmadik lehetőség az, hogy a listát egy
kulcstáblán keresztül nézzük, melyekben a mutatók sorrendje a rendezést követi.
Ez a legpraktikusabb megoldás, az adatszerkezetet nem tördeljük szét, fizikai
helye nem változik, ugyanakkor egy listára több rendezett kulcstábla is írható.
Egyetlen hátránya: minden újabb kulcstábla újabb memóriát foglal el. Fizikai
rendezésre sokféle rendezési eljárást kidolgoztak már. Minden konkrét rendezési
feladatnál ki kell választani a megfelelő rendezési eljárást. Ha nagy
adathalmazzal dolgozunk, vagy a rendezést gyakran újra és újra végre kell
hajtani, akkor gyors rendező eljárást illik használni. Ha ez nem áll fent,
akkor egyszerűbb vagy lassúbb eljárást is választhatunk.
3. Objektumok
3.1 Objektumok tulajdonságai
A nem objektumorientált programokban a
legnagyobb gond a modulok és az adatok összehangolt használata. Már a moduláris
programozásnál említettük, hogy a megfelelő adatszerkezet megalkotása nem a
legegyszerűbb feladat. Minél kevesebb globális változó alkalmazására kell
törekedni. A modulokban pedig az ugyanolyan, vagy hasonló funkciókat
ellátó változókat következetesen mindig ugyanúgy deklaráljuk, illetve
használjuk. Ennek az összehangolt változóhasználatnak az objektumok
deklarálásában is nagy szerepe van. Konvenciók segítenek a névválasztásban. A
mutatók nevét mindig „P”, az objektumtípus nevét mindig „T”, a mezőneveket „F”,
az inicializáló paraméterneveket „I” betűvel kezdjük. Az objektumok szerkezete
a rekordok szerkezetéhez hasonlít legjobban.
Az objektumokban együtt találhatók az adatok (ennyiben hasonlít a
rekordra) és a modulok, amelyeket itt metódusoknak fogunk hívni. Az objektum
adatain és adataival csak saját metódusai dolgozhatnak. Így már olyan egyszerű
esetben is, mint az értékadás illetve értéklekérdezés, metódusokat kell írnunk.
Az objektum mezőinek (adatainak) lekérdezésére GET kezdetű, beállítására
(értékadásra) SET kezdetű metódusneveket használunk. Kezdőértéket beállító
eljárás neve: INIT. Ezen szabályok betartása az objektum sérthetetlenségét,
zártságát biztosítják. Az objektumban az adat mindig megőrződik, az objektum
mindig emlékszik saját állapotára. Az objektumoknak vannak olyan metódusai,
amelyek kívülről elérhetők. Ezek alkotják az objektum interfészét. Az
objektumokat ezek segítségével megszólítjuk, aminek hatására állapotuk
megváltozik, esetleg az objektum további objektumokat aktivizál.
Ha egy programban több objektumot használunk, akkor azok ugyanazon
üzenetre (megszólításra, metódushívásra) még az ugyanolyan típusú (ugyanazon osztályba
tartozó) objektumok is, lehet, hogy másképpen reagálnak. A reakciójuk ugyanis
függhet az állapotuktól. Ha pedig az objektumhierarchia különböző szintjein
vannak, akkor ugyanolyan néven más-más tartalommal rendelkező metódusai
lehetnek, ezáltal természetes módon nem ugyanúgy reagálnak az eseményekre. Ezt
a tulajdonságot nevezzük sokoldalúságnak, vagy idegen szóval polimorfizmusnak.
Objektum orientált programjainkban az adatok és modulok
összekapcsolása, mint azt láthatjuk, már megoldódott. De hogyan segíti a
kód-újrafelhasználást az objektum. Nagyon elegáns módon. Ha van egy
objektumtípusunk bizonyos mezőkkel és bizonyos metódusokkal, és ez számunkra
még nem elég jó, akkor a meglévő típusból készítünk egy leszármaztatott típust
(ezt az OOP nyelvek természetesen biztosítják), és az örökölt mezők mellé
újakat vehetünk fel, vagy újabb metódusokat írhatunk, sőt a régi metódusokat
fölül is definiálhatjuk. Ezt a mechanizmust öröklődésnek nevezzük. Az
öröklődésnél van még egy fontos momentum, amely a
Unit-okban tárolt modulok kód-újrahasznosításán is túltesz. Ha egy régi
modulunk már nem elég jó, akkor vagy teljesen újat írunk helyette, vagy
kijavítjuk a régi kódlistát. Ha csak használatra kapunk egy modulokat
tartalmazó Unit-ot, akkor erre nem biztos, hogy lehetőségünk
van. Ha nem adták át a kódlistát, akkor azt bizony nem tudjuk megtenni.
Objektumok esetén, mint az a fentiekből is sejthető, nem kell ismerni a kódot,
egyszerűen leszármaztatjuk az újat és az említett módon
kijavítjuk. A nem általunk írt objektumokkal kapcsolatban épp az lehet a
legnagyobb probléma, hogy nem tudjuk, milyen képességekkel rendelkeznek. Ha
nincs kódlista, nem tudjuk visszafejteni. Ezért létfontosságú a pontos
dokumentáció. Az általánosan használt rendszereknek a megismerését jelentősen
akadályozza az, hogy nem írnak le mindent a gyártók (szakmai féltés, jogi
dolgok) a teljes körű használathoz, csak annyit, amennyi feltétlen szükséges.
Nekünk kell kutatgatni, megsejteni, mit – hogyan valósítottak meg az adott
rendszerben.
Tehát összefoglalva az objektumok
tulajdonságait:
-
adat és kód
együtt található az objektumban,
-
zártság, az objektum adataihoz csak interfészeken keresztül
férünk hozzá,
-
sokoldalúság vagy polimorfizmus,
-
öröklődés.
3.2 Objektumok a Pascal
nyelvben
Objektumot a Pascal nyelvben csak főprogramban vagy Unit-ban deklarálhatunk, modulban nem. A deklaráció
típusdeklaráció, ezért a program Type szakaszába tartozik.
Type
TPont= Object
End;
Az
Object
lefoglalt szó, strukturált párja az End. Az objektum mezőinek és metódusainak a leírását e két
kulcsszó között kell megadni. A metódusoknak csak a fejét írjuk itt le,
kifejtése még a deklarációs szakaszban, azaz a főprogram Begin-je előtt történik. Mivel
több objektumnak is lehet ugyanolyan nevű metódusa, a kifejtési szakaszban a
metódus nevét minősíteni kell az osztály nevével (típusnevével). Unitban a
kifejtés az Implementációs szakaszban foglal helyet.
Type
TPont= Object
Fx, Fy: Integer;
Procedure Init(Ix, Iy:
Integer);
End;
Procedure
TPont.Init(Ix, Iy: Integer);
Fx
:= Ix;
Fy:= Iy;
End;
Mintánkban egy inicializáló metódust irtunk le, mellyel az objektum
mezőinek értéket adtunk. Természetesen további mezői és metódusai is lehetnek
az objektumnak, illetve e mintában lévő mezők is a felhasználástól függően
lehetnek különböző jelentésűek (síkbeli koordináták, két paramétere valamely
algebrai műveletnek, két szín koordináta stb.) Az objektum típusdeklarációja –
a legtöbb nyelvben osztálydeklarációnak nevezik – csak egy minta az objektum
összetételére, szerkezetére. Objektumot, Pascal terminológia szerint egyedet, Var segítségével vehetünk fel
programunkban, a Type
szakasz után.
Var Pont1, Pont2: TMinta;
Az Pont1 illetve Pont2 már két objektum, mely a TPont
osztályba tartozik, vagy másképpen a típusa TPont, és
amelyet moduljainkban illetve a főprogramban már használatba is vehetjük.
Például:
Pont1.Init( 50,
100);
POnt2.Init(150,
200);
Ezen két metódushívás után az Pont1 objektumban Fx
értéke 5, Fy értéke 10, az Pont2 objektumban az Fx értéke 15, az Fy értéke pedig
20 lesz mindaddig a program futása alatt, ameddig azt egy újabb metódushívással
meg nem változtatjuk. Azaz az objektum megőrzi adatait. Természetesen
programjainkban, egy adott cél érdekében, majd jól átgondolt szerkezetű
objektumok fogunk deklarálni.
3.3 Statikus és dinamikus
objektumok
Keletkezési körülményeik illetve
élettartamuk szerint megkülönböztethetünk statikus és dinamikus metódusokat. Az
előző pontban statikus objektumot deklaráltunk. Nézzük meg, hogyan alakíthatjuk
dinamikussá.
Type
PPont= ^TPont;
TPont= Object
Fx, Fy: Integer;
Procedure Init(Ix, Iy:
Integer);
End;
Ezzel a deklarációval a PPont révén egy TPont-ra mutató pointerünk lett. Ennek segítségével
dinamikusan deklarálhatunk objektumot programunkban. Most az Pont1 és Pont2
mutató típusú.
Var Pont1, Pont2: PPont;
A programban ezért előbb helyet kell neki foglalni a
New eljárás segítségével.
New(Pont1);
New(Pont2);
Hivatkozni az objektumok metódusaira pedig a
következőképpen kell:
Pont1^.Init(
50, 100);
Pont2^.Init(150,
200);
Ha a dinamikus objektumunkra már nincs szükségünk, akkor Dispose eljárással megszüntetjük, felszabadítva helyét a Heap-en.
Dispose(Pont1);
Dispose(Pont2);
Deklarációinkban a típusnevek első betűi az egyszerű (statikus)
deklarációra (T) és a dinamikus változóra (P) utalnak, mint ahogy azt már
fentebb is említettük.
3.4 Öröklődés
Az objektumok legérdekesebb
tulajdonságai az öröklődéssel kapcsolatosak. Öröklődés révén az objektumok
hierarchikus rendbe tartoznak. Ha az ős objektumot egy rajzon legfölül
képzeljük el, a leszármazottakat pedig alatta, akkor a hierarchia egy fához
hasonlít. A fa gyökere az az objektum, amely minden
objektumnak közös őse, de ő már nem leszármazottja egyetlen objektumnak sem. Ez
leggyakrabban egy absztrakt osztály, amelyből példányt sohasem hozunk létre.
Minden objektumnak csak egy őse lehet (legalábbis a Pascal nyelvben), de
bármely objektumnak lehet több leszármazottja is. Az öröklési lánc hosszára
semmilyen megkötés nincs, de egyébként is az a helyzet, hogy egy négy-öt
generációs öröklési lánc már olyan bonyolulttá tud válni, amelyet nehéz átlátni
és kezelni. Nagyon ritka tíz vagy annál hosszabb öröklési lánccal rendelkező
hierarchia a mindennapi gyakorlatban. Minden objektum örökli összes ősének
összes adatát és metódusát, és természetesen birtokolja saját adatait és
metódusait is. Nézzük hogyan valósítható meg mindez Pascal nyelven. Induljunk
ki a már fentebb deklarált TPont osztályból,
származtassunk belőle olyat, amely kört valósít meg. A körnek a középpontja
lehet az ősének a két mezője, de a kör megadásához még egy adatra, a sugarára
is szükség van.
Type
TKor= Object(Tpont)
Fr:
Integer;
Procedure Init(Ix, Iy,
Ir: Integer);
Procedure Meretez(Dr: Integer);
End;
Az öröklés tényét az Object kulcsszó után zárójelbe tett TPont
osztálynév biztosítja. Figyeljük meg a változást. Az adatoknál a TPont adatmezőit már nem kell felsorolni. Ennek ellenére a TKor típusú objektumnak van Fx és
Fy mezője. Mivel a kör megadásához három adat kell,
az Init metódust újra kellett írni. Az Init kifejtése lehet például a következő:
Procedure
TKor.Init(Ix, Iy, Ir:
Integer);
Inherited
Init(Ix, Iy);
Fr:= Ir;
End;
Az Inherited
egy lefoglalt szó, jelentése: az öröklési láncban az a – ebben az esetben Init nevű - metódus, amelyet legelőször megtalál a fordító,
miközben az öröklési hierarchiában az aktuális objektumtól a gyökér fele halad.
A teljesség kedvéért felvettünk egy új metódust is, a Meretez-t.
Ennek az lesz a funkciója, hogy a kör sugarát megváltoztassa az átvett
értékkel.
Procedure
TKor.Meretez(Dr: Integer);
Fr:= Fr + Dr;
If Fr<0 Then Fr:= 0;
End;
Ennek a metódusnak a meghíváskor a kör sugara
Dr-el megváltozik, amely pozitív Dr
esetén növekedést, negatív esetén csökkenést jelen. A feltétel a negatív
körsugár elkerülést szolgálja. Örökléskor tehát az utódban újabb mezőket
deklarálhatunk, fölülírhatjuk az ős metódusait, valamint új metódusokat
írhatunk.
3.5 Késői kötés, virtuális
metódusok
Az objektumok
viselkedésének a legszebb, legérdekesebb de egyúttal talán a legnehezebben
érthető része következik most. Először is bővítsük mindkét objektumunkat a
következőképpen:
Type
TPont= Object
Fx, Fy: Integer;
Procedure Init(Ix, Iy:
Integer);
Procedure
Show;
Procedure
Hide;
Procedure
Mozog(Dx, Dy: Integer);
End;
Type
TKor= Object(Tpont)
Fr:
Integer;
Procedure Init(Ix, Iy,
Ir: Integer);
Procedure Meretez(Dr: Integer);
Procedure
Show;
End;
Az új metódusok kifejtése:
Procedure
TPont.Show;
PutPixel(Fx, Fy);
End;
Procedure
TPont.Hide;
ClearDevice;
End;
Procedure
TPont.Mozog(Dx, Dy: Integer);
Hide;
Fx:= Fx + Dx;
Fy:= Fy + Dy;
Show;
End;
Procedure
TKor.Show;
Circle(Fx, Fy,
Fr);
End;
Nézzük az új metódusok magyarázatát. A
Show metódusok megjelenítik a grafikus képernyőn az objektumokat. A PutPixel eljárás az aktuális színnel, egy pontot rajzol a
képernyő (Fx, Fy)
koordinátájú helyére. A Circle eljárás az aktuális szinnel, (Fx, Fy)
középponttal Fr sugarú kört rajzol. A Hide metódus eltünteti az objektumot, jelenleg a lehető
legegyszerűbb módon, letörli a grafikus képernyőt. Ezek után a Mozog metódus
már eléggé nyilvánvaló: a régi helyéről letörli a pontot, megváltoztatja a
helyét, majd újra kirajzolja. Könnyen belátható, hogy ebben az egyszerű
hierarchiában a pont mozgatása hibátlanul működik.
De nézzük mi a helyzet a körrel, mely
metódusok működnek helyesen, melyek nem. Nyilvánvaló, hogy mindhárom újonnan
definiált hibátlanul működik. De mi a helyzet az örökölt metódusokkal? Mivel a Hide csak egyetlen képernyőtörlés, ezáltal tökéletesen
alkalmas a kör eltüntetésére. De nézzük meg mi a helyzet
a Mozog metódussal, merthogy ezt is érti a TKor,
hiszen örökölte. Mit várunk el egy TKor.Mozog(10,
10) metódushívástól. Azt hiszem eléggé természetes módon azt, hogy a kör
változtassa meg a helyét. Csakhogy ez a fenti kódok alapján nem fog
bekövetkezni. Nézzük meg hogy miért nem. Amikor meghívjuk a TKor.Mozog(10, 10) metódust, akkor a TPont.Mozog(10,
10) kezd végrehajtódni. Letörlődik a képernyő, a középpont értékei
megváltoznak, eddig még minden rendben. De ekkor egy Show hívása történik.
Vajon mit rajzol a gép. Mivel mit sem sejt arról, hogy a körnek saját, a
ponttól teljesen különböző rajzoló metódusa van, rajzol egy pontot, és ezzel
befejezi a mozgást. Azaz a körünk ponttá zsugorodott, abszolút helytelenül. Azt
kellene elérni, hogy a metódusok visszataláljanak a hívó objektum saját
osztályában található ugyanolyan nevű (ez esetben Show) metódusához. De vegyük
észre, hogy itt egy öröklési folyamat is zajlik. A „szegény” pont még mit sem
sejt arról – mert nem is sejtheti – hogy őbelőle valaha még kör lesz, és
ugyebár a kör nem ugyanaz a látvány, mint a pont. Amikor a program fordítása
zajlik, a fordítóprogram még nem tudhatja – ha nem tudatjuk vele – hogy a
későbbiekben, egy leszármazottja, egyik metódusát másmilyen tartalommal
szeretné használni.
Azokat a metódusokat, amelyeket eddig megismertünk, statikus
metódusoknak fogjuk nevezni. Azért hívják őket statikusnak, mert a fordítás
pillanatában a metódus ugrási címe a memóriában már ismert és rögzített érték
lesz. Fenti példánkban viszont, ha azt szeretnénk, hogy a kör mozogjon, egy
olyan helynek a meghívását kellene a fordítónak beírni, amit még nem ismer (a TPont fordítása pillanatában), és amely esetleg majd nem is
fog létezik. De a megismerés lehetőségét bele kellene programozni kódjainkba.
Ennek a problémának a kezelésére alkották meg a metódusok másik csoportját, a
virtuális metódusokat. A fordító a virtuális metódusok fordításakor üres
tárhelyeket tart fent egy táblázatban (Virtuális Metódus Táblában, azaz a VMT-ben), melybe a program futási ideje alatt kerülnek bele
a használható ugrási címek, mely címeket az objektumok a saját magukra mutató
pointerükkel töltenek fel (Self paraméterrel), mely
paramétert látens módon magukkal hordoznak. Így talál vissza a program a hívó
objektum osztályleírásához, és találja meg a megfelelő virtuális metódust.
Szokás ezért ezt a jelenséget futás idejű kapcsolatnak, vagy késői kötésnek is
nevezni. A virtuálissá tételnek a nyelvi megvalósítása nagyon egyszerű: a
deklarációban azokat a metódusokat, amelyeket a leírt módon szeretnénk
használni, virtuálissá kell tenni úgy, hogy a metódusfej után még a következő
lefoglalt szót írjuk: Virtual.
Azaz:
Type
TPont= Object
Fx, Fy: Integer;
Procedure
Init(Ix, Iy: Integer);
Procedure
Show; Virtual;
Procedure
Hide;
Procedure
Mozog(Dx, Dy: Integer);
End;
Type
TKor= Object(Tpont)
Fr:
Integer;
Procedure Init(Ix, Iy,
Ir: Integer);
Procedure Meretez(Dr: Integer);
Procedure
Show; Virtual;
End;
Mint láthatjuk ebben a deklarációban a
Show metódust kellett virtuálissá tenni. Ha egy metódus virtuális, akkor minden
leszármazottjában, minden ugyanolyan nevű metódus is virtuális. Ha nem tesszük
ki a leszármazott deklarációjában a Virtual minősítőt, akkor fordítási hibát kapunk. A kifejtési
szakaszban semmi változtatásra nincs szükség. Felmerülhet ezek után a kérdés,
vajon mikor kell egy metódust virtuálissá tenni? Nem könnyű a kérdésre
válaszolni. Lényegében bármelyik metódus lehet virtuális, csak akkor a
memóriában több helyet foglal el az osztályleírás, mégpedig a VMT-hez szükséges mérettel. VMT-je
ugyanis csak virtuális metódust tartalmazó osztálynak van. Talán az egyik
legjobb válasz az előző kérdésre az, hogy azokat, amelyek fölüldefiniálását meg
szeretnénk engedni, illetve amely metódusokról látszik, hogy a későbbiekben
fölüldefiniálása kívánatos. Absztrakt metódust (amelynek üres a törzse és csak
a származtatás lehetősségét biztosítja) szinte bizonyos, hogy virtuálissá kell
tenni. Biztosan nem kell virtuálissá tenni az Init
metódust, mert egy új osztály e nélkül szinte biztosan nem definiálható,
létrehozáskor ez biztosan meghívódik már az objektum osztályból (nincs a már
részletesen leírt visszadefiniálás), ezért virtuálissá tétele nem indokolt.
Most térjünk át a dinamikus
objektumokra, nézzük meg, milyen újdonságot rejtenek még a létlehozásuk és
megszüntetésükkel kapcsolatos metódusok. Ennek érdekében megismerkedünk további
két metódustípussal, a konstruktorral és a destruktorral. Dinamikus objektumot eddig csak a New
eljárással tudtunk létrehozni. Eláruljuk, hogy a New-nak
van egy úgynevezett kiterjesztett formája, amely egyrészt nem eljárás, hanem
függvény (a visszaadott érték a létrehozott objektum típusra mutató Pointer),
másrészt nem csak egy paramétere lehet, hanem kettő. Kiterjesztett New-t csak
olyan dinamikus objektummal kapcsolatban használhatunk, amelynek van konstruktora. A konstructor
mindig statikus, vagyis nem lehet virtuális, mert akkor meghívása előtt egy konstruktort kellene meghívni. Általában az Init eljárást szoktuk konstructorrá
minősíteni. Nyelvileg ez úgy történik, hogy az Init
eljárás fejében a Procedure
kulcsszót a Constructor
szóra cseréljük. Azaz a deklaráció:
Type
PPont= ^Tpont;
TPont= Object
Fx, Fy: Integer;
Constructor
Init(Ix, Iy: Integer);
Procedure
Show; Virtual;
Procedure
Hide;
Procedure
Mozog(Dx, Dy: Integer);
End;
Ezek után, ha a
programban létre szeretnénk hozni egy PPont típusú
dinamikus objektumot, akkor a következő lehetőségek egyikével élhetünk:
1. New(Pont1); Pont1^.Init(50, 100);
2. New(Pont1, Init(50, 100));
3. Pont1:=
New(PPont, Init(50, 100));
Az első megoldás gyakorlatilag
nem ajánlott, hiszen lényegében ugyanazt teszi, mint a második, csak két
lépésben. Ráadásul az első megoldásnál a New eljárásának eredményességéről még
külön meg kellene győződni azért, mert lehet, hogy nem sikerült a helyfoglalás
a memóriában, akkor viszont programhiba következik be. Ha ugyanis ha nincs elég
hely, akkor a Pont1 mutató értéke Nil lesz, ennek használata viszont legtöbb esetben a gép
lefagyását eredményezi. Erre az esetre a Pascal nyelv a Fail
eljárást küldi segítségül, amely szabályos hibakezelést tesz lehetővé azáltal,
hogy kilép a konstruktorból. Ezt az eljárást
alkalmazni viszont csak a 2. és 3. esetben lehet, amikor a konstruktor
(ez esetben az Init) a New második paramétereként
szerepel. A második és harmadik megoldás között aszerint
választhatunk, hogy a létlejött dinamikus objektum mutatójára szükségünk van-e
vagy sem. A 2. esetben igen, 3. esetben a létrejövő mutatót rögtön átadjuk egy
másik, pl. listára felfűző eljárásnak. Figyeljük meg, hogy a 3. esetben a New
első paramétere az osztályra mutató pointer. A kiterjesztett New helyfoglalását
gyakorlatilag a konstruktor végzi, mely természetesen
figyelembe veszi a VMT méretét is.
Most nézzük mi a helyzet a dinamikus
objektumok megszüntetésével. Erre való a destruktor,
amelynek standard metódusneve a Done. Mivel ilyen
metódus még nincs a TPont leírásában, ezért ezzel
most az osztályleírást kiegészítjük:
Type
PPont= ^Tpont;
TPont= Object
Fx, Fy: Integer;
Constructor
Init(Ix, Iy: Integer);
Procedure
Show; Virtual;
Procedure
Hide;
Procedure
Mozog(Dx, Dy: Integer);
Destructor
Done;
End;
Remélem
nem meglepő, hogy a Dispose eljárásnak is van egy
kiterjesztett formája, amely kétparaméteres. Csak akkor van értelme destructort írni, ha van az osztálynak virtuális metódusa,
de akkor valószínű, hogy van konstruktora és VMT-je is. Ha a dispose második
paramétere a Done, akkor a Done
elvégzi a memória felszabadítást, melyhez a méretet a VMT-ből
olvassa ki.