Eljárások és paraméterek

2018.01.17. · tudomány

Eljárás, utasítás, eljárás elindítása

Minden programot receptként kell felfognunk, szakszóval eljárásként (angolul: procedure), amiben a számítógépnek előírjuk bizonyos tevékenységek végrehajtását. De egy programon belül is több különböző eljárás szokott lenni (például a sokat emlegetett modularitás jegyében), amik már a program futása során kerülhetnek sorra. Itt van például egy eljárás Pascal programozási nyelven:

procedure kukucs;
begin
writeln('Kukucs!');
end

Az első sor közli, hogy egy eljárást fogunk megadni, amit a kukucs névhez társítunk, ezen a néven tudunk majd rá hivatkozni. A begin és az end közötti rész az eljárás törzse, ez a voltaképpeni recept. A példában a recept egyetlen utasításból áll, a writeln azt jelenti, hogy egy betűsorozatot (karaktersorozatot, stringet) kellene a képernyőre kiírni, utána zárójelben áll az, hogy mi legyen az a string (a Kukucs! string; az előtte és utána álló aposztróf csak annak a jele a Pascalban, hogy ami közöttük van, azt vegye stringnek).

A programon belüli sok eljárás közül ki kell tüntetni azt, aminek a program elindításakor elsőnek kell elindulnia, az összes többit majd vagy ez indítja el, vagy valamelyik, amit ez indított el, stb. A különböző programozási nyelvekben más-más módon tüntetik ki ezt az első, fő eljárást, például sok nyelvben az a konvenció, hogy main-nek kell elnevezni. (Pont a Pascalban nem.) Hogy hogyan indít el egy eljárás egy másikat? Hát pont úgy, mint a mi kukucs eljárásunk a writeln nevű eljárást, egyszerűen a nevén szólítva, vagyis az eljárás neve egyben a recept egyik utasításaként használható. (Hogy mi a helyzet a név után álló zárójeles dologgal, arra mindjárt visszatérek.)

Paraméterek

Nehéz bugyutább eljárást elképzelni, mint a fenti kukucs nevű, nemcsak azért, mert semmi értelme sincs annak, amit csinál, hanem azért is, mert mindig ugyanazt csinálja. Nehezen tudnék most hirtelen ezen kívül olyan programot mondani, ami minden egyes futásánál pontosan ugyanazt hajtja végre. (Egyébként az ilyen kukucs-hoz hasonlító programokat a világ Hello, world! programnak hívja, ha valaki a neten ilyeneket keres különböző nyelveken.) Még ha arra a szintén egyszerűnek látszó programra gondolok, ami lelövi az operációs rendszert és kikapcsolja a gépet, az is minden alkalommal mást csinál, méghozzá elég bonyolult dolgokat, mert attól függően, hogy éppen milyen programok futnak a gépeken, mást és mást kell neki magának lelőnie, meg mindezt könyvelnie is kell.

photo_camera Grafika: Tóth Róbert Jónás

De mitől fog ugyanaz a program a különböző futásaikor más és más viselkedést mutatni? Attól, hogy a viselkedését a külvilágtól teszi függővé, hogy körül tud nézni, és attól függően, amit maga körül lát, más- és másképpen reagál. Mindezeket a külső tényezőket, amik befolyásolni tudják a program viselkedését, a program paramétereinek nevezzük. És ahhoz, hogy a paramétereknek megfelelően a program változtatni tudja a magatartását, kell hogy legyenek benne változó, a paraméterektől függő részek. Ugyanez igaz nemcsak az egész programra, hanem minden egyes eljárásra.

A paraméterek nagyon sokféleképpen tudnak eljutni a programhoz. (Pontosabban úgy mondanám: sokféleképpen lehet megadni az értéküket, vagy sokféleképpen lehet őket rögzíteni.) Például lehet, hogy a program párbeszédet folytat velünk, a felhasználókkal, és mi pötyögjük be a billentyűzetről azokat az adatokat, amik rögzítenek bizonyos paramétereket. Vagy lehet, hogy a weben át kérünk meg valami távoli szervert, hogy futtassa a programot (ez történik mindig, amikor „megnyitunk” egy weboldalt), és így a neten át küldjük el a szervernek (és így a programnak) a megfelelő paramétereket. Például úgy, hogy az oldalon válaszolunk kérdésekre, ráklikkelünk dolgokra stb., vagy úgy, hogy magának a webcímnek a végére írunk információkat (ilyenkor a cím után áll egy „?” karakter, és utána azok a dolgok, amiknek az alapján a szerver majd paramétereket fog beállítani). Olyan is van, hogy a program (persze a mi utasításunkra) szépen megnyit bizonyos file-okat, onnan kiolvas mindenféle információkat, és ezek adják meg egyes paramétereknek az értéküket.

A programon belül az egyes eljárásoknak még egy fontos módon lehet paramétereket megadni: ahogy a kukucs nevű Pascal-programban láttuk, az eljárás neve után tett zárójelpáron belül – ha több paramétert adunk meg, akkor általában vesszőkkel elválasztva. (Persze nem minden nyelvben pont így néz ez ki, de majdnem.) Vagyis a writeln nevű eljárás minden egyes futásakor mást csinál, attól függően, hogy milyen stringet akarunk vele kiíratni, ez neki az egyetlen paramétere. És abban az utasításban, amivel elindítjuk, a neve után zárójelben rögzíthetjük ezt a paramétert (a mi példánkban a paraméter értéke a Kukucs! string volt). Az ilyen módon megadott paramétereket szokásosan argumentumoknak hívjuk (ugyanezt a szót szokták használni a nyelvészek `vonzat' értelemben, a matematikában pedig így szokták nevezni azokat az értékeket, amikre egy függvényt alkalmazunk).

Értékek, függvények

Nagyon gyakori, sőt jellemző, hogy az eljárásokat valaminek az előállítására, kiszámítására használjuk, és annak a bizonyos előállított, kiszámított dolognak kitüntetett szerepet tulajdonítunk, és úgy hívjuk, hogy az eljárás (visszatérési) értéke (angolul: (return) value). Annyira gyakori ez, hogy általánosan elterjedt dolog ezt tekinteni alapesetnek. Pont a Pascal programozási nyelvben ez nem így van, ott a procedure kulcsszó olyan eljárást vezet be, aminek nem „ad vissza” ilyen értéket, ahogy a fenti kukucs sem, és a function (magyarul: függvény) kulcsszóval kell bevezetni azt, amelyik értéket ad vissza (és az eljárás törzsén belül a return kulcsszóval bevezetve adjuk meg, hogy mi az az érték, amit visszaad). Nem véletlen, hogy az a jelölés, ahogy az argumentumokat megadjuk (zárójelek között, vesszővel elválasztva) ugyanaz, mint a matematikában a függvényalkalmazás jelölése.

Az eljárások által kiszámított értékeknek olyan nagy jelentőséget tulajdonítanak, hogy külön programozási „paradigmának” számít, amikor minden eljárásra függvényként tekintenek, és az ilyen nyelveket funkcionálisnak nevezik, az eljárásközpontú procedurálisakkal szembeállítva. A funkcionális paradigmában úgy értelmezik függvényként az eljárásokat, hogy azok egy vagy több argumentumhoz értéket rendelnek, és még érdekes függvényműveleteket is lehet rajtuk végezni. (És ha egy eljárás nem számít ki, nem állít elő semmi érdekeset, akkor is úgy fogják fel, minta ilyesmit csinálna.) A ma legnépszerűbb programozási nyelvek mind ehhez a funkcionális irányzathoz tartoznak, és például az eljárások megadásánál is erre emlékeztető kulcsszavakat használnak, ha egyáltalán használnak ilyen kulcsszót (pl. fun vagy function, def, define).

Amikor a programunkban olyan utasítást használunk, ami egy függvényként felfogott eljárás elindítását írja elő, akkor magát ezt az utasítást úgy használjuk a program szövegében, mint az értéket, amit visszaad. Például:

procedure kukucs;
begin
writeln(UpperCase('Kukucs!'));
end

Az UpperCase nevű függvény argumentuma egy string, az értéke pedig ugyanennek a stringnek olyan változata, amiben a kisbetűk helyett a megfelelő nagybetűk vannak. És látható, hogy az az utasítás, ami az UpperCase elindítását írja elő (a Kukucs! stringgel mint argumentummal), vagyis az UpperCase('Kukucs!') kifejezés, mint a matematikában is a hozzá hasonlók, egyben azt az értéket is jelöli, amit az a függvény visszaad (és ez az érték lesz a writeln eljárás argumentuma).

És végül egy személyes jellegű megjegyzés. Én nem szeretem az eljárások függvényszerű felfogását. Nem is annyira azért, mert az eljárások végrehajtásának lehetnek mellékhatásai (például hogy valami megjelenik a képernyőn), míg az „igazi”, matematikai függvények esetében ezt nehéz értelmezni. És nem is csak azért, mert igen gyakoriak azok az eljárások, amik nem valaminek a kiszámítására valók, hanem – ugyanúgy, ahogy a mi kukucs-unknak – csak mellékhatásai vannak, amit matematikai függvények esetén végképp nehéz értelmezni. Másmilyenek az én fogalmi problémáim.

Egyrészt az, hogy az eljárásoknak nemcsak azok a paraméterei, amiket argumentumként megadhatunk nekik, hanem például azok is, amiket a futásuk közben mondjuk egy file-ból kiolvashatnak. Másrészt az, hogy az eljárások, amíg nem rögzítjük a paramétereiket, fogalmilag sok-sok lehetséges folyamatnak (ezek általánosításának) felelnek meg, és ahogy egyik-másik paramétert rögzítjük, úgy egyre specifikusab folyamatnak, egyre kevesebb lehetséges futásnak. Ha minden paraméterüket pontosan megadjuk, akkor pedig fogalmilag egyetlen konkrét folyamatot kapunk, nem pedig azt a bizonyos kiszámított értéket, ami a return-nel bevezetett utasításban van. De sebaj, attól, hogy a filozófiájukkal nem vagyok jó barátságban, még én is vidáman használom a funkcionális programozási nyelveket.

A szerző nyelvész, az MTA Nyelvtudományi Intézetének főmunkatársa.

link Forrás