Függvény
A Programozás Wiki wikiből
A programozásban általában függvénynek nevezünk egy olyan programkód-részletet, ami valamilyen eredményt szolgáltat. A programnyelvek zömében a függvények nyelvi szinten is megjelennek. Általában a függvények eredménye egyetlen jól körülhatárolt érték, adat, vagy objektum, amit a függvény visszatérési értékének nevezünk. Sok példa van arra is, mikor a függvény ezen felül más hatást is kelt, egyéb adatokat is számol, objektumokat módosít, vagy kimenetet produkál. A visszatérési értéket a függvény általában nem önmagában, hanem valamilyen paramétereknek megfelelően állítja elő. A paraméterek is meg szoktak jelenni a forráskódban. Ennek gyakori jelölése az, hogy a függvény neve után egy zárójelben fel van sorolva a paraméterek neve és típusa.
A függvények többnyire saját (lokális) változókkal és típusokkal is rendelkezhetnek, vagyis önálló alprogramokként foghatók fel. Bizonyos nyelvekben azokat az alprogramokat is függvénynek hívják, amik nem generálnak visszatérési értéket. A kódban ezt általában egy void kulcsszóval jelzik.
Tartalomjegyzék |
[szerkesztés] Argumentumok
A függvény eredményének képzéséhez szükséges paramétereket argumentumoknak is hívjuk. Egy függvényhez általában néhány (1-5) argumentum tartozik, 0 is megengedett. A túl sok argumentum nehezen olvashatóvá teszi a kódot, ezért ilyen esetekben javasolt az argumentumok összevonása valamilyen objektumba, vagy rekordba. A szigorúan típusos nyelveknél a függvény deklarációjában meg kell adni az argumentumok típusait is. A deklarációban szereplő argumentumokat formális paramétereknek, a függvényhíváskor átadottakat aktuális paramétereknek hívjuk. A deklaráció egyfajta elő-ellenőrzésre használható, a fordító nem engedélyez olyan függvényhívást, amiben az aktuális paraméterek nem felelnek meg a deklarációban jelzetteknek.
Egy függvény argumentumai átadásuk módja szerint többfélék lehetnek:
- Érték szerinti átadásról beszélünk, ha a függvény csak az argumentum értékével dolgozhat. Általában a függvénytörzs módosíthatja az értéket, de ez nem lesz hatással a program többi részére. A fordítók többnyire a program vermében helyezik el ilyenkor az értéket. Nagy adatstruktúrák esetén ezért kerülendő, mert növeli a veremhasználatot és a másolás időt is igényel.
- Cím szerinti átadásról beszélünk, ha a függvény a paraméter futás közbeni memóriacímét kapja meg. Ilyenkor a hivatkozott adatot, vagy objektumot a címen keresztül éri el, vagyis az esetleges módosítások visszahathatnak a függvényen kívüli programrészre. Ebbe a kategóriába tartoznak általában a konstans (const), referencia (var), vagy kimeneti (out) paraméterek. Van, hogy a programnyelv a cím szerinti átadásnál elrejti a konkrét mutatót, az argumentumok ilyenkor a függvény változóiként kezelhetők.
[szerkesztés] Függvényhívás
Egy függvény futtatását függvényhívásnak nevezzük. Az aktuális paraméterek a függvény által elérhető helyre (többnyire a program vermébe) kerülnek, és a vezérlés átadódik a függvénynek. Az elvégzi a feladatát, kiszámolja a visszatérési értéket, elérhetővé teszi (a verembe az argumentumok helyett), majd visszaadja a vezérlést a hívó programrésznek. Az általa lefoglalt memória terület felszabadul.
A függvény meghívásakor a verembe lefoglalásra kerül a függvény visszatérési címe,a lokális deklarációjában létrehozott változók, a függvény fejlécében meghatározott paraméterlistában szereplő argumentumok (paraméterek) is. Ha a függvény elvégezte a szükséges számításokat, és elérte a függvény törzsének végét jelző záró részt (ez pascalban az End;), a visszatérési cím alapján visszaadja a vezérlést a hívónak.
Egy függvény futás közben önmagát is hívhatja, amit rekurziónak nevezünk. Más függvényeket is felhasználhat a számításhoz. Ebben az esetben a második függvény paraméterei is bekerülnek a verembe, annak adódik át a vezérlés, és így tovább. Az akár több tucatnyi elemből álló láncot hívási veremnek (call stack) nevezzük.
Egy program akár más forrásnyelven írt könyvtár függvényeit is hívhatja, ha egyező hívási módokat használnak. A hívási módot a deklaráció mögötti kulcsszavakkal (cdecl, stdcall) jelöljük.
[szerkesztés] Egyéb érdekességek
[szerkesztés] Makró
Egyes nyelvekben (pl. C) a függvényekhez első látásra nagyon hasonló, úgynevezett makrók is használhatók. Ezeknél fontos tudni, hogy itt nincs szó veremhasználatról, a makrókat az előfordító a "hívás" helyén a makró törzsére cseréli a paraméterek helyettesítésével.
Bizonyos programnyelvek megengedik a hívás nélküli (inline) függvények használatát. Ezek a makrókhoz hasonlóan működnek, azzal a különbséggel, hogy a fordító nem a forráskódban, hanem a lefordított tárgykódban végzi a helyettesítést.
[szerkesztés] Függvény mint változó értéke
Sok programnyelvben (többek közt az összes funkcionális nyelvben) lehetőség van a függvények értékként való kezelésére. Ez lehetővé teszi, hogy a függvényt változóban tároljuk, vagy paraméterként adjuk át más függvénynek. Szigorúan típusos nyelvekben (pl. C, Pascal, C#) a függvény mint érték típusát a szignatúrája (az argumentumok és a visszatérési érték típusai) határozza meg.
Ezt a funkciót gépi kódra fordító nyelvekben általában úgy valósítják meg, hogy a függvény első gépi kódú utasításának a címét tárolják le az adott változóban. Interpreteres nyelvekben a függvényeket egyébként is az interpreter valamilyen belső adatszerkezete reprezentálja, így ennek a címe használható a változó értékeként.
Nézzünk példaképpen egy Python nyelvű számológép programot, ami könnyen bővíthető új műveletekkel:
#Definiáljuk a műveleteket def add(x, y): return x+y def sub(x, y): return x-y def mul(x, y): return x*y def div(x, y): return x/y #A műveletvégző függvényeket a műveleti jelekhez rendeljük egy szótárban operators = {'+':add, '-':sub, '*':mul, '/':div} op1 = float(raw_input('Az első operandus:')) op2 = float(raw_input('A második operandus:')) operator_str = raw_input('A műveleti jel:') #Kikeressük a megfelelő függvényt operator_function = operators.get(operator_str) if operator_function is None: print 'Hiba: érvénytelen műveleti jel!' else: #Meghívjuk a változóban tárolt függvényt result = operator_function(op1, op2) print 'Az eredmény:', result
Példa a függvények paraméterként való átadására: készítsünk függvényt, amely egy másik függvény értékeit számolja ki egy listába!
#A függvény eredménye egy lista a függvény értékeiből a 0..(length-1) egész számokra def list_function(fun, length): result = [] for i in range(length): result.append(fun(i)) return result #Példa függvény: négyzetre emelés def square(i): return i*i #Példa függvény: Fibonacci számok def fibo(i): if i in (0,1): return i else: return fibo(i-2)+fibo(i-1) print list_function(square, 5) #Kiírás: [0, 1, 4, 9, 16] print list_function(fibo, 10) #Kiírás: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Egyes nyelvekben lehetőség van úgynevezett névtelen függvények, vagy más néven lambdák használatára. Ezek olyan speciális kifejezések, amelyek egy függvényt adnak értékül; ezt az értéket egyből változóhoz rendeljük, vagy egy másik függvénynek adjuk át argumentumként. Ezt a megoldást akkor szokás alkalmazni, ha az adott függvény viszonylag rövid, és csak egyetlen helyen használjuk a kódban. (Egyéb esetekben rontja az olvashatóságot.)
[szerkesztés] Függvény példák
[szerkesztés] SearchString
[szerkesztés] Példa Object Pascal nyelven:
function SearchString {a függvény neve} ( {argumentumlista} const Arr: array of String; {konstans argumentum, név és típus} const Item: String; CaseSens: Boolean; {érték szerint átadott paraméter} out Index: Integer {cím szerint átadott kimeneti paraméter} ): Boolean; {a visszatérési érték típusa logikai} var i: Integer; {lokális változó} begin {függvénytörzs kezdete} for i := 0 to Length(Arr)-1 do begin {A visszatérési érték (Result) változóként használható} if CaseSens then Result := Item = Arr[i] else Result := 0 = CompareText( Item, Arr[i] ); if Result then begin Index := i; {a cím szerint átadott paraméter változóként használható} Exit; {kilépés a függvényből, a vezérlés visszakerül a hívóhoz} end; end; Index := -1; Result := false; end; var IsThere: Boolean; Index: Integer; begin IsThere := SearchString( ['Bob','Joe','Jim'], 'jim', false, Index ); {a függvény hívása az aktuális paraméterekkel} end.
[szerkesztés] Példa Haskell nyelven:
stringSearch :: [String] -> String -> Bool -> (Bool,Int) stringSearch slist str caseSens = res slist (False,0) where res [] _ = (False, (-1)) res (x:xs) (b,i) = if cond then (cond,i) else res xs (cond,(i+1)) where cond = (if caseSens then str == x else map toLower str == map toLower x) main = putStrLn $ show $ stringSearch ["Bob","Joe","Jim"] "Jim" False
[szerkesztés] Sum
[szerkesztés] Példa Turbo Pascal nyelven:
Itt érdekességként elmondható, hogy a függvény visszatérési értékének típusát a fejlécben, az eredményét a függvénytörzsben rögzíteni kell, úgy hogy a függvény azonosítóját az értékadás jel baloldalán kell elhelyezni, majd megadni az értékadás jelet ( := ) , és meg kell adni azt a kifejezést amely a függvény visszatérési értékének eredménye lesz.
... function_name:= expression; end;
Nézzünk meg egy egyszerű függvényt amely összead 2 számot, amely lehet egész(Integer) és valós (Real) is.
Var n1:Integer;
n2,e:Real;
Function Sum(num1,num2:Real):Real;
BeginSum:=num1 + num2;
End;Begin {main}
n1:=100;
n2:=12.023;
e:=Sum(n1,n2);
End.
[szerkesztés] Példa Haskell nyelven:
sum' :: (Num a, Show a) => a -> a -> a sum' = (+) main' = putStrLn $ show $ sum' 100 12.023