Ötödik lecke

5. lecke: Alprogramok




Kétféle alprogramot különböztetünk meg. Léteznek eljárások és függvények. A kettő közötti különbség az, hogy a függvénynek van visszatérési értéke az eljárásnak nincs. De mik is ezek valójában? Tegyük fel van egy programrész amit több helyen is fel szeretnénk használni. Akkor nem kell mindenhova bemásolni, hanem elég egyszer megírni és hivatkozni rá. Sőt még változókat is lehet átadni neki. Az alprogramokat a főprogram előtt kell deklarálni. Lássuk akkor először az eljárásokat.

Eljárás: Nem ad vissza semmilyen értéket, de természetesen lehet átadni neki paraméterben változókat. Az eljárás neve és paraméterlistája alkotja a fejét. Az eljárás törzse lesz az a rész ahova kódot írnuk és ez fog végrehajtódni, amikor meghívjuk az eljárást. Nézzünk rá egy példát:


 Program eljaras;
 Uses crt;
{ Itt kezdődik az eljárás }
 Procedure kiir_zold(szoveg : string; X,Y : integer); { Az eljárás feje. }
 Begin
      TextColor(green);
      GoToXY(X,Y);
      Write(szoveg);
 End;
{ Itt ér véget az eljárás. }
 BEGIN
      ClrScr;
      { Itt használom fel az eljárásomat a programban. }
      kiir_zold('Hello Vilag!', 40, 5);
      { Itt mégegyszer megívom, csak más paraméterekkel. }
      kiir_zold('Ezt a programot HUNIgor készítette.', 2, 20); 
      ReadKey;
 END.

Lássuk mi is történt. A procedure kulcsszót követően meg kell adni a nevét. Ezután zárójelben a formális paraméter listát. Mitől lesz formális? Mivel konkrét értéket nem tartalmaz, csak jelezzük szándékunkat, hogy amikor meghívjuk majd az eljárást akkor ilyen típusú és ennyi darab változót szeretnénk neki átadni, valamint, hogy az eljáráson belül ilyen néven fogom hívni. Amilyen sorrendben adtam meg a változókat itt, olyan sorrendben kell majd megadni az aktuális paramétereket is amikor meghívom az eljárást. Az aktuális paraméter tehát az amikor az eljárást felhasználom valahol (a továbbiakban meghívom) és a neve után zárójelben megadom neki a konkrét értéket. A fenti példában ez a: ('Hello Vilag!', 40, 5). Sajnos eleinte könnyű keverni ezt a két fogalmat. De jusson eszünkbe, hogy a formális az nem konkrét, tehát csak név + típus. Az aktuális pedig konkrét érték, ami azért aktuális mert az eljárást több helyen is meghívhatom és így hívásonként más-más értékeket is megadhatok. Tehát mégegyszer a formális paraméter az eljárás deklarciójában található, míg az aktuális paraméter az eljárás meghívásánál.

Természetes igény lehet, hogy egy alprogramon belül nem csak azokat a változókat használjuk amik szerepelnek a paraméterei között. Lehet létrehozni változót az alprogramon belül is.


 Procedure kiir_random_szin(szoveg : string; X,Y : integer); { Az eljárás feje. }
 var szin : integer;
 Begin
      szin := Random(6);
      TextColor(szin);
      GoToXY(X,Y);
      Write(szoveg);
 End;

Itt most véletlen számot adunk meg színnek. Volt szó korábban arról, hogy nem csak a színek angol neveit lehet használni, hanem számokat is. Tehát szin változót az eljáráson belül hoztuk létre. Ez felvet egy nagyon fontos kérdést. Hol használhatjuk a szin változót? Használhatunk-e egy olyan változót amit nem az eljáráson belül hoztunk létre? Ezt hívják láthatósági problémának. Lássuk a megoldását!

A Pascalban létezik globális változó. Ezek azok, amikkel korábban találkoztunk. Ezeket a változókat bárki elérheti, akár eljárás, akár függvény, akár maga a főprogram. Ettől lesz globális. Szeretném azonban hangsúlyozni, hogy egy alprogramban nem szép globális változókat használni. Nagyobb programoknál hatalmas káoszt tud okozni, ha rászokunk erre. Így törekedjen mindenki a lehető legkevesebbszer ilyenhez folyamodni. Az alprogramoknak lehet lokális változója is. A példánkban a szin is ilyen. Ezt a változót csakis az alprogramon belül érjük el. Tehát szin felhasználása a főprogramban vagy más alprogramokban nem lehetséges. Vagy mégis? Mi a helyzet akkor ha egy alprogramon belül létrrehozok még egy alprogramot? Mondjuk így:


Program eljaras;
Uses crt;

{ Ennek ugyan az a neve mint az alprogramban található változónak. }
var szin : integer; 

Procedure kiir(szoveg : string; X,Y : integer);
var szin: integer; { Két változó ugyanazzal a névvel? }

    Procedure setup_color; { Ez itt egy eljárás az eljáráson belül. }
    var i : integer; { Neki is van egy változója. }
    Begin
         i := Random(6) + 1;
         szin := i;
         textColor(szin);
    End;
    
Begin
     setup_color;
     GoToXY(X,Y);
     Write(szoveg);
End;

BEGIN
     Randomize;
     ClrScr;
     szin := 0;
     kiir('Hello Vilag!', 40, 5);
     writeln;
     writeln(szin);
     ReadKey;
END.

Remélem a példa megfelelően illusztrálja a helyzet komolyságát. Van két szín nevű változónk. Ráadásul az eljárásunkon belül is lett egy eljárás aminek szintén van egy változója és használja szin változót. Na de melyiket? Mindenki megnyugtatására az alprogramon belül létrehozott szin változó elfedi a főprogramban létrehozottat. Így amikor az alprogramon belül írunk olyat, hogy szin akkor az eljáráson belül létrehozottat használja. Ha a főprogramban írunk olyat, hogy szin akkor a globálisat használja. A setup_color a kiir eljáráson belül van. Így számára (mármint a setup_color számra) a szin változó globális. Hogy érthetőbb legyen nézzük meg az alábbi táblázatot.


Főprogram

 var szin, x: integer;

procedure a

 var szin: integer;
 
procedure b

 var i: integer;
 begin
      i := { Megengedett, hiszen 
      ebben az eljárásban hoztam létre. }
      
      szin := { procedure a -ban 
      létrehozott változó elfedi a globálisat. }
      
      x := { mivel x globális és nincs semmi ami 
      elfedi így globális x kapna itt értéket. }
 end;

begin i := { HIBA!! i itt még nem "él", ezért fordítási hibát eredményez. } szin := { Az itt létrehozott szin változó, NEM a globális. } x := { mivel x globális és nincs semmi ami elfedi így globális x kapna itt értéket. } end;

 BEGIN
      i := { HIBA!! i itt még nem "él", ezért fordítási hibát eredményez. }
      
      szin := { Itt globális színt haszálja, mivel a procedure a -ban 
      létrehozott el van rejtve. }
      
      x := { mivel x globális és nincs semmi ami elfedi így globális x 
      kapna itt értéket. }
 END.


Függvény: Abban különbözik az eljárástól, hogy van visszatérési értéke. A függvény célja tehát, hogy a kapott paraméter(ek) alapján előállítson egy értéket. Vegyük például az abszolút érték kiszámítását. Ugyan a Pascal tartalmazza ezt és felesleges megírni (lásd: Pascal help -> index -> Abs) de példának jó lesz. Egy szám abszolút értéke önnmaga, ha a szám nemnegatív és ellentettje, ha negatív. A függvénytől elvárjuk, hogy a kapott változó abszolút értékét előállítsa és visszaadja azt. Nézzük az alábbi kódot!


Function abszolut_ertek(szam : integer) : Integer;
Begin
     If (szam>=0) Then
     begin
          abszolut_ertek := szam;
     end
     Else
     begin
          abszolut_ertek := (-1) * szam;
     end;
End;

BEGIN { Főprogram }
     eredmeny := abszolut_ertek(l); { Itt használjuk a függvényünket. }
     WriteLn(abszolut_ertek(-17); { Itt is. }
     abszolut_ertek(77); { Szabályos, de nem sok értelme van. }
END.

Az első sor a függvény feje. Procedure kulcsszó helyett function van. Majd a függvény neve, zárójelben a függvény formális paraméterlistája. Eddig semmi különös. A fő különbség a zárójel utáni rész. Kettőspont után jelezzük függvényünk visszatérési értékének a típusát. A függvényekben ugynúgy hozhatunk létre változókat, sőt másik függvényt is, sőt eljárást is, de akár eljárásban is hozhatunk létre függvényt. A változók láthatóságára ugyanaz érvényes mint az eljárások esetében.

Felhasználásuk különbözősége az eljárásokéhoz képest abból ered, hogy van visszatérési értéke, amit az esetek többségében használunk is valamire. Ezért szerepelnek legtöbbször értékadás jobb oldalán, esetleg write paraméterében. Meghívhatjuk ugyan úgy is mint az eljárást, nemtörődve a visszatérési értékével. Egy függvény esetében azonban ez ritka, hiszen pont azért készítünk belőle függvényt, mert kell a visszatérési értéke.

Ennyit az alprogramokról. Ha bármi nem volt világos, vagy kimaradt, akkor írjátok meg a vendégkönyvbe. Ha túlságosan is érthető és (már fárasztóan) szájbarágós akkor azt is. Következő lecke még ettől is izgalmasabb lesz, úgyhogy most már senki se kapcsoljon el, a mindent magába szívó tisztasági betét és inteligens mosópor után jövünk vissza!




Vissza