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.

Argumentumok[szerkesztés]

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.

Függvényhívás[szerkeszté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.

Egyéb érdekességek[szerkesztés]

Makró[szerkesztés]

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.

Függvény mint változó értéke[szerkesztés]

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.)

Függvény példák[szerkesztés]

SearchString[szerkesztés]

Példa Object Pascal nyelven:[szerkesztés]

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.

Példa Haskell nyelven:[szerkesztés]

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

Sum[szerkesztés]

Példa Turbo Pascal nyelven:[szerkesztés]

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;
Begin
  Sum:=num1 + num2;
End;

Begin {main}
  n1:=100;
  n2:=12.023;
  e:=Sum(n1,n2);
End.

Példa Haskell nyelven:[szerkesztés]

sum' :: (Num a, Show a) => a -> a -> a                                                                                               
sum' = (+)

main' = putStrLn $ show $ sum' 100 12.023

Kapcsolódó témakörök[szerkesztés]