4. fejezet

Megjegyzések. Írni utálunk, ezért törekszünk az újra felhasználható programrészekre. Függvények a matematikában és Jávában. Paraméterek, visszatérési értékek és változó láthatósági szabályok.

Programjaink most már kezdenek kellõen bonyolultak lenni ahhoz, hogy magyarázat nélkül ne értsük õket. Minden programozási nyelv, köztük a Jáva is lehetõséget ad arra, hogy ember számára értelmes magyarázó szövegeket, megjegyzéseket tegyünk a kódba. A megjegyzéseket a számítógép egyszerûen figyelmen kívül hagyja, semmilyen hatása nincsen a program mûködésére. Nem is neki szólnak, hanem a program olvasójának. Jávában kétfajta megjegyzés van. Az egyik /* karakterekkel kezdõdik és a legközelebbi */ karakterekig tart, a programban bárhol állhat, több soros is lehet. Pl.

/* Itt egy megjegyzés */
i = 1; /* A sor végén egy megjegyzés */
/* Hosszú,
  több sort
  átfogó megjegyzés
*/
i /* a változó, amit használunk */ = /* értékadó utasítás */ 2 /* 2 egy jó érték */;

Az utolsó variációt nem kell követendõ példának tekintened, csak azt akartam vele megmutatni, hogy ez a fajta megjegyzés tényleg mindenhol lehet. A másikfajta megjegyzést // jelek vezetik be és a sor végéig tart. Kevesebbet kell írni vele, rövid megjegyzéseknél ezért szeretik. Példák:

// Egy rövid kis megjegyzés
i = 3; // Végül is igazában 3-at szeretnénk i-be
// Ha hosszú megjegyzést akarunk,
// Minden sor elejére kell a //
// jel.

Feladat

Írjunk egy programot, ami kiírja az y=x2 függvény értékeit x=(-1,1) intervallumban, 0.1-es lépésenként. Megjegyzésekkel tedd világosabbá, mikor mi történik! A feladat nem okozhat nehézséget, azért a biztonság kedvéért ellenõrizd a megoldást!

A megoldásban felhasználtunk egy újdonságot, a += operátort. Ez az operátor hozzáadja a bal oldalán levõ változóhoz a jobb oldalán levõ értéket, tehát kiveszi a változó értékét, hozzáadja az operátor jobb oldalán levõ kifejezés értékét és visszatárolja a változóba.

x += 0.1;

tehát egyenértékû

x = x + 0.1;

kifejezéssel.

A kiírásban furcsa eredményeket találhattál, pl. íme egy részlet:

...
-0.20000000000000015^2=0.04000000000000006
-0.10000000000000014^2=0.01000000000000003
-1.3877787807814457E-16^2=1.9259299443872359E-32
0.09999999999999987^2=0.009999999999999974
...

Nagyjából stimmel, de mik ezek a furcsa értékek a végén? És pláne, miért nem kapunk 0-t (habár 10-16 elég közel van hozzá ...)? Tudnod kell, hogy a számítógép számára csak az egész, int értékek pontosak. Mivel a gép másként tárolja a számokat, mint az ember, nem biztos, hogy az olyan szép, kerek értékek, mint a 0.1 a gép számára is ugyanolyan szépek. A 0.1 pont egy olyan szám, amit a gép nem tud teljesen pontosan ábrázolni, ezért az összeadások során apró hibák halmozódnak fel. Éppen ez okból a lebegõpontos számokat sose vizsgáljuk egyenlõségre, mert könnyen lehet, hogy az egyenlõség sohase teljesül. Esetünkben például x sohase lesz pontosan 0 vagy 1, ezért a furcsa határértékösszehasonlítás a for ciklusban. Hadd ismételjem meg, hogy ez a probléma csak a lebegõpontos számoknál léphet fel, egészeknél soha, ez egy újabb ok arra, hogy mindenhol egészeket használjunk, ahol csak lehet.

Akárhogy is, nem erre akartam kilyukadni. Maga a kiszámítandó függvény a program közepében van, ha cserélni akarnánk, ki kellene keresnünk a programszövegbõl. Rosszabb, ha a függvény netán bonyolultabb lenne és nem lehetne egy sorban kiszámolni, netán esetleg belsõ változókat használna, a kiszámítandó függvény kódja teljesen összekeveredne a táblázatnyomtató program kódjával. Ezen okból a Jáva rendelkezésünkre bocsátja a saját függvények definiálásának lehetõségét. Igazából már van két saját függvényünk, de azok vadító kék színben tündököltek. Lássuk elõzõ programunk módosítását olymódon, hogy az y = x2 számítását végzõ részt kiemeljük saját függvénybe!

public class x2ertek_2 {

  double fuggveny( double xi ) {
    double yi;
    yi = xi * xi;
    return yi;
  }

  public void prog( String args[] ) {
     double x;
     for( x = -1 ; x < 1.001 ; x += 0.1 ) {
       System.out.println( x+"^2="+fuggveny( x ));
     }
  }

  public static void main( String args[] ) {
    x2ertek_2 e = new x2ertek_2();
    e.prog( args );
  }
}

Ha a programot lefuttatjuk, persze ugyanazt az eredményt kapjuk, mint az elõzõ példában. Mik akkor a változások? Vegyük mindenekelõtt észre a kék sorok drasztikus megfogyatkozását! Vegyük továbbá észre, hogy a táblázatnyomtató ciklusunkban könnyed eleganciával kijelentettük, hogy fuggveny( x ) által visszaadott értéket is ki akarjuk írni. Ez egy függvényhívás.A függvényhívás során a számítógép egy alprogram végrehajtásával folytatja munkáját. Az alprogramot valahol lezárják egy visszatérõ utasítással és megmondják, mi az érték, amit az alprogram visszaad. Ekkor a számítógép elõkeresi, hol hagyta abba a fõprogram végrehajtását és a függvényhívást helyettesíti azzal az értékkel, amit az alprogram visszaadott. A függvény tehát bármilyen kifejezésben állhat, ahol a függvény visszatérési értékének típusa megegyezik az adott kifejezésben várt adattípussal. Például ha van egy függvényünk, ami double-t ad vissza, akkor az felhasználható double számokkal dolgozó kifejezésben.

double fuggv() {
...
}
...

double x;
x = 3.14*fuggv()+2.5;

Maga a függvény deklarációja bárhol lehet a programban, a függvényhívás elõtt és után is. Tilos azonban ugyanazt a függvényt kétszer deklarálni ugyanazzal az aláírással vagyis ugyanazzal a névvel, visszatérési értékkel és paraméterlistával. Azonos nevû, de különbözõ aláírású függvények (tehát például ahol az egyiknek nincs paramétere, a másiknak pedig van) különbözõnek számítanak, mert a Jáva a függvényhívás alapján rájön, hogy melyik függvényt kell meghívnia. Példa:

// Elsõ függvény
int fuggv() {
  ...
}

// Második függvény
int fuggv( int i ) {
  ...
}

int i = fuggv();     // Ez az elsõ függvényt hívja meg
int k = fuggv( 2 );  // Ez a második függvényt hívja meg

A függvénynek lehet egy speciális visszatérési értéke, a void. Ez azt jelenti, hogy a függvény nem ad vissza értéket, az ilyen függvényt nem lehet kifejezésben felhasználni, de kifejezésen kívül meg lehet hívni. Pont ezen a módon kell alprogramokat, procedurákat definiálni.

void fuggv2() {
...
}
...

fuggv2();

A függvényeknek lehetnek bemeneti paramétereik is. Ezek azok az értékek, amelyeket átadunk az alprogramnak, hogy számoljon belõle kimeneti értéket. A bemeneti paramétereket a függvényfejben soroljuk fel.

double fuggv3( double elso_param, int masodik_param ) {
  double eredm;
...
  return eredm;
}

...
double eredm;
eredm = fuggv3( 3.14, 4 );

Amikor a függvényt meghívod, bonyolult dolgok játszódnak le.

Hasonlatos ez ahhoz, mint amikor a cikkedhez ki akarsz számolni egy értéket, de csak a szám érdekel. Néhány sajtpapíron elkészíted a számításokat, a számot beírod a cikkbe, majd a sajtpaírokat eldobod. Így mûködik a függvény is, a részeredmények érdektelenek a függvény lefutása után és ami még fontosabb, a függvény belsõ ügyei nem zavarják a program futását.

A függvénybõl természetesen újabb függvényt is meghívhatsz és a hívásra ugyanazok a szabályok vonatkoznak. Ezt nem csinálhatod a végtelenségig, valamekkora korláta van annak, milyen mélységben hivogathatják a függvények egymást, de ez sok ezres mélység.

Lássuk, megértetted-e!

Feladat

Olvasd el az itt látható programot és mondd meg nekem, mi a nyomtatási kép! Utána próbáld is ki és találd ki az okát, ha netán tévedtél volna. Segítség: nézd meg, melyik függvényhívás melyik fuggv függvényt hívja meg. Ahol a függvényhívásban paramétert adunk át, ott a paraméterrel rendelkezõ fuggv fut, ahol nem, ott a paraméter nélküli.

A megjegyzések használata persze magánügy, de talán a fenti példa is demonstrálja, hogy legalább a függvények fejét (tehát a deklarációjának az elejét, a visszatérési értékének, nevének és paramétereinek megadását) mindenképpen ajánlott megjegyzéssel ellátni, milyen paramétert vár a függvény, azokkal mit csinál és mit ad vissza.

Feladat

Bergengóciában az adórendszert lényegesen leegyszerûsítették és a Totális Adótábla XXVIII. lapja a következõképpen néz ki:

-100 fbtk* a jövedelem 9%-a
100-250 9+a 100 fbtk fölötti rész 13%-a
250-750 30+a 250 fbtk fölötti rész 25%-a
750- 180+a 750 fbtk fölötti rész 50%-a

*- fbtk: konvertibilis fabatka, éves szinten

Az utolsó pillanatban a Tüntetõ Bergengóc Anyák Antidemokratikus Szövetségének sikerült keresztülvinnie követeléseit és a családosokoknak kedvezõ intézkedésekkel egyszerûsítették a táblát. Ezek szerint a 1 gyerek nevelése esetén az adó 2%-át, kettõ esetén 8%-át, 3 és a felett gyerekenként 4%-át, de legfeljebb 25%-ot engednek el. Írjunk most egy programot, amit meghívhatunk az éves jövedelemmel és a gyerekek számával és az megmondja az adónkat! Emlékezzünk a 3. lecke Negativ programjára, hogyan értékeltük ki ott a paramétereket! Lebegõpontos számot még nem értékeltünk ki, ezért leshetsz egy kicsit a megoldásban. A programunkban igyekezzünk olyan függvényt írni, ami a leginkább lerövidíti a programot! Íme a megoldás, de csak a paraméterek kiértékelését lesheted ki belõle, a többit neked kell megoldanod!

Ellenõrzõ kérdések

  1. Mire szolgálnak a megjegyzések a programban?
  2. Mi a különbség a /* */ és a // megjegyzésszintaktika között?
  3. Miért nem vizsgáljuk a lebegõpontos számokat egyenlõségre?
  4. Mi a függvény aláírása?
  5. Mi a void típus?
  6. Mik játszódnak le egy függvényhíváskor?
  7. Hogyan adja vissza a függvény a visszatérési értékét?
  8. Mi történik a függvény által deklarált változókkal?
  9. Mi történik, ha a hívó függvény és a hívott függvény megegyezõ nevû változókat használ?
  10. Milyen mélységben hívhatják függvények egymást?

Kovetkezo lecke Tartalomjegyzek