Függvények

BecomeAnXcoder – Függvények

Bevezető

Az eddigi leghosszabb programkód, amivel találkoztunk, csupán 5 soros volt. De a több ezer soros programok sem ritkák, megértésük nem egyszerű feladat, ezért már a kezdeti lépéseknél meg kell ismerkednünk az Objective C programok felépítésével.
Ha egy program utasítások végeláthatatlan sorozata lenne, akkor az nagyon megnehezítené a hibakeresést. Az is előfordulhat, hogy bizonyos programrészek többször előfordulnak egy programban. Ha itt valahol hiba van, akkor minden egyes előfordulás során ki kell javítani azt. Ez egy rémálom lehet, elég csak elfelejteni egy, vagy több részletet kijavítani! Keresni kell tehát azokat a lehetőségeket, amelyek segítségével áttekinthetőbbé tehetjük a programunkat, könnyebbé a hibakeresést.

A probléma megoldása abban rejlik, hogy feladatuk szerint csoportosítsuk az utasításokat. Megmutatjuk ezt egy példán. Tegyük fel, hogy van egy utasításkészletünk a kör kerületének kiszámítására. Ha egyszer már letesztelted, hogy ez a kódrészlet helyesen működik, akkor a későbbiekben már nem lesz szükség arra, hogy ebben a kódrészletben hibát keressél. Ezt az utasításkészletet függvénynek fogjuk nevezni, nevet adunk neki és a neve alapján tudunk majd hivatkozni rá, azaz meghívjuk a függvényt. A függvények használata annyira alapvető, hogy legalább egy függvény garantáltan van minden programban: ez pedig a main() függvény. A main() függvényt keresi a fordítóprogram először, ez mutatja meg számára azt a kódot amit a program indításakor értelmeznie kell.

A main() függvény

Nézzük meg a main() függvény felépítését részletesebben. [1]

//[1] 
main() 
{ // A main() függvény törzse. Ide kell beilleszteni a programkódot. }

Az [1.1] mutatja a függvény nevét, jelen esetben ez a “main”, amit egy nyitó és egy záró zárójel követ. A “main” egy foglalt név és ahogy említettük, a main() függvény minden programban kötelezően jelen van. Amikor saját függvényeket definiálsz, szinte semmilyen megkötés sincs arra, hogyan fogod azt elnevezni. A zárójelek fontos szerepet játszanak, de erről majd egy kicsit később fogunk beszélni ebben a fejezetben. Az [1.3, 1.5] sorokban kapcsos zárójeleket láthatunk. Ezek közé a kapcsos zárójelek közé kell írnunk a programkódot: { }. A kapcsos zárójelek közötti részt a függvény törzsének nevezzük. Az első fejezetből átvett megfelelő kódrészletet helyeztünk el a [2]-es példában.

//[2] 
main() 
{ // Változó deklarálások float pictureWidth, pictureHeight, pictureSurfaceArea; // Inicializáljuk a változókat, értéket adunk nekik
pictureWidth = 8.0; pictureHeight = 4.5; // Itt végezzük el az aktuális számítási feladatot pictureSurfaceArea = pictureWidth * pictureHeight; }

Az első függvényünk

Amennyiben az összes programkódot a main() függvénybe helyeznénk, akkor éppen abba a csapdába kerülnénk, amitől szerettük volna megóvni magunkat, egy strukturálatlan, nehezen kezelhető programkódot kapnánk. Ehelyett strukturáljuk a programunkat, és a main() program mintájára készítsünk egy circleArea() függvényt [3].

//[3] 
main() 
{
float pictureWidth, pictureHeight, pictureSurfaceArea;
pictureWidth = 8.0; pictureHeight = 4.5; pictureSurfaceArea = pictureWidth * pictureHeight; } circleArea() // [3.9] {
}

Idáig ebben nem volt semmi ördöngösség, de a [3.9] sorban kezdődő új függvény egyelőre nem csinál semmit. Figyeljük meg, hogy az új függvény specifikációja a main() függvény törzsén kívül helyezkedik el. Másképpen megfogalmazva, ezek a függvények nincsenek egymásba skatulyázva.

Az új circleArea() függvényt a main() belsejéből kell meghívni, Nézzük meg hogyan [4].

//[4] 
main() 
{
float pictureWidth, pictureHeight, pictureSurfaceArea,
circleRadius, circleSurfaceArea; // [4.4]
pictureWidth = 8.0;
pictureHeight = 4.5; circleRadius = 5.0; // [4.7]
pictureSurfaceArea = pictureWidth * pictureHeight;
// Itt hívjuk meg az új függvényt!
circleSurfaceArea = circleArea(circleRadius); // [4.10]
}

Megjegyzés: a program egésze itt nem látható (lásd [3]).

Paraméterátadás

Definiáltunk float típusú változókat [4.4], inicializáltuk a circleRadius változót, értéket adtunk neki [4.7]. A legérdekesebb a [4.10]-es sor, ahol a circleArea() függvényt meghívtuk. Ahogy látható, a circleRadius változónevet zárójelek közé tettük. Ez a circleArea() függvény argumentuma. A circleRadius változó értéke itt kerül átadásra a circleArea() függvény részére. Amikor a circleArea() függvény elvégzi a feladatát, azaz kiszámolja a kör kerületét, visszatér az eredménnyel. Ehhez még ki kell egészíteni a circleArea() függvényt [3] a megfelelő utasításokkal [5].

Megjegyzés: itt most csak a circleArea() függvény látható.

//[5] 
circleArea(float theRadius) // [5.1] 
{
float theArea;
theArea = 3.1416 * theRadius * theRadius; // a sugár négyzetét megszorozzuk a pi számmal [5.4]
return theArea; // [5.6]
}

Az [5.1] sorban definiáljuk, hogy a circleArea() függvény számára egy float típusú bemenetre van szükség. Amikor ezt megkapja, a theRadius nevű változóba kerül a bemeneti érték. Még egy változóra lesz szükségünk: theArea, ahol a számítás eredményét fogjuk tárolni [5.4], ezért ezt is deklarálnunk kell [5.3]. Ugyanúgy tesszük ezt ahogy a változókat deklaráltunk a main() függvényben [4.4]. Érdemes megfigyelni, hogy a theRadius változó deklarálása a zárójelek között történik [5.1]. Az [5.6] sorban az eredmény visszatér abba a sorba, ahonnan a függvény meg lett hívva. Ezért az eredmény a [4.10] sorban a circleSurfaceArea változó értéke lesz.
Már majdnem teljesen készen vagyunk az [5]-ös példában bemutatott függvénnyel kapcsolatban, egy dolog azért még hiányzik. Nem határoztuk meg a függvény visszatérési értékének típusát. A fordítóprogram megköveteli ezt tőlünk, ezért nem tehetünk mást, mint beállítjuk azt float típusra [6.1].

//[6] 
float circleArea(float theRadius) //[6.1]
{
float theArea;
theArea = 3.1416 * theRadius * theRadius;
return theArea;
}

A [6.1] sor megadja, hogy a függvény visszatérési értéke, azaz a theArea változó értékének a típusa float. A programozónak garantálnia kell, hogy a circleSurfaceArea változó a main() függvényben [4.4] ugyanilyen típusra legyen definiálva, és akkor a fordítóprogram nem zsémbeskedhet velünk.

Nincs minden függvénynek argumentuma. Ha nincs, a zárójelekre () akkor is szükség van, bár ekkor közöttük nincs semmi.

//[7]
int throwDice()
{
int noOfEyes;
// Ez egy 1 és 6 közötti véletlenszámot generáló programkód
return noOfEyes;
}

Visszatérési értékek

Olyan függvény is van, aminek nincs visszatérési értéke. Az ilyen függvény típusa: void. Ebben az esetben a return utasítás opcionális. Ha használod a return utasítást, akkor azt nem követheti érték, vagy változónév ebben az esetben.

//[8] 
void beepXTimes(int x);
{
// Ide egy olyan programkód kerül, amelyik x alkalommal sípol
return;
}

Lehetséges, hogy egy függvény egynél több argumentummal is rendelkezik, ilyen a pictureSurfaceArea() függvény. ilyen esetben az argumentumokat vesszővel kell elválasztani egymástól.

//[9] 
float pictureSurfaceArea(float theWidth, float theHeight)
{
// területszámító programkód
}

Megállapodás szerint a main() függvény visszatérési értékének egy integer-nek kell lenni a következő jelentéssel. A 0 (nulla, [10.9]) visszatérési érték jelentése az, hogy a program probléma nélkül lefutott. Mivel a main() függvény egy egész értékkel tér vissza, ezért az “int” kódot kell írnunk a main() rész elé [10.1].

Tekintsük át egyben eddigi programunkat!

//[10] 
int main() //[10.1]
{
float pictureWidth, pictureHeight, pictureSurfaceArea,
circleRadius, circleSurfaceArea; pictureWidth = 8;
pictureHeight = 4.5;
circleRadius = 5.0;
pictureSurfaceArea = pictureWidth * pictureHeight;
circleSurfaceArea = circleArea(circleRadius); // [10.8]
return 0; // [10.9] }

float circleArea(float theRadius) // [10.12]
{
float theArea;
theArea = 3.1416 * theRadius * theRadius;
return theArea; }

Hozzuk működésbe a dolgokat

A [10]-es példában láthatjuk, hogy van egy main() függvényünk [10.1] és egy másik, általunk definiált függvényünk is [10.12]. Amennyiben megpróbáljuk lefordítani ezt a kódot a fordítóprogram még mindig akadályba ütközik. A [10.8]-as sorban nem fogja megérteni a circleArea() függvényhívást. Vajon miért? Valóban, a fordítóprogram elkezdi értelmezni a main() függvényt és egyszer csak egy olyan részt talál benne, ami ismeretlen számára. Megáll ezen a ponton, nem keres tovább, csupán egy figyelmeztetést küld. Nagyon egyszerűen segíthetünk ezen a problémán azzal, hogy deklaráljuk ezt a függvényt még az int main() utasítás előtt [11.1]. Nincs ebben semmi nehézség, ez pontosan ugyanolyan, mint a [10.12]-es sor, attól, csak egy picit tér el: pontosvessző van a végén. Ezután a fordítóprogram már nem fog meglepődni, amikor ezzel a függvényhívással találkozik.

//[11]
float circleArea(float theRadius); // [11.1] függvény deklaráció
int main() { // a Main függvény kódja kerül ide ... }

Megjegyzés: a teljes programkód itt nem látható (lásd [10]).

Hamarosan valóságban is le fogjuk fordítani ezt a programot, de előtte még álljon itt pár megjegyzés.

A programíráskor tartsuk szem előtt, hogy egy kódrészletre a későbbiekben is szükségünk lehet majd.
Beilleszthetünk egy rectangleArea() függvényt, ahogy a [12]-es példában látjuk, és ezt a függvényt meghívhatjuk a main() függvényben. Még akkor is hasznos lehet az, ha egy programrészletet egy függvénybe helyezünk, amennyiben azt csupán egyszer használjuk fel. A main() könnyebben olvasható ezáltal. A hibakeresés során könnyebben megtalálhatjuk a hibás részt ezen a módon, mert lehetséges, hogy nem kell végignézni utasítások hosszú sorozatát, hanem ehelyett esetleg elegendő csak a függvényben található néhány utasítást áttanulmányozni, amiket könnyű megtalálni köszönhetően a nyitó és záró kapcsos zárójeleknek.

//[12] 
float rectangleArea(float length, float width) //[12.1]
{
return (length * width); //[12.3] }

Ebben a példában azt is bemutatjuk, hogyan lehet egy egyszerű utasítással egy sorban elvégezni a számítási feladatot és rögtön visszaadni annak az eredményét [12.3]. A [10.14] sorban a theArea változó ezért valójában felesleges, csak arra használtuk, hogy bemutassuk a változó deklarálást a függvényen belül.

Már ezeken az egyszerű példákon keresztül is nagyon jól látható, hogy ha nem változtatjuk meg a függvény deklarációját (azaz az első sorát), akkor ugyanúgy tudunk rá hivatkozni akkor is ha a függvény programkódját megváltoztatjuk. Például megváltoztathatjuk egy változó nevét a függvényen belül, és a függvény ugyanúgy fog működni és ez egyáltalán nem érinti a program többi részét. Ez azt is jelenti, hogy valaki más meg tudja írni a függvényt és te tudod azt használni anélkül, hogy ismernéd annak a belső felépítését. Csak annyit kell tudnod, hogy hogyan kell használni a függvényt. Tehát ismerned kell a:

- függvény nevét
- a függvény argumentumainak számát, sorrendjét és típusát
- mi a függvény visszatérési értéke (pl. a téglalap területének értéke) és az eredmény típusát

A [12]-es példában ezek rendre:- rectangleArea
- két argument, mindkettő float, ahol az első a hosszúságot, a második a szélességet jelöli
- a függvénynek van visszatérési értéke, aminek a típusa float (ami az utasítás első szavából látható) [12.1]).

Rejtett változók

Érdemes megjegyezni, hogy a függvény belseje láthatatlan a main programból és más függvényekből is. Ez azt jelenti, hogy egy függvényben a változók értékei nincsenek kapcsolatban más függvények változóival, még akkor sem, ha ugyanaz a nevük. Ez az Objective C egyik legalapvetőbb jellegzetessége, amit az 5. fejezetben tovább fogunk tanulmányozni. De most már ideje, hogy futtassuk a [10]-es példában szereplő programot.

 

eredeti oldal