Az Eiffel programozási nyelv

Alprogramok, eljáráshívás, paraméterátadás

Alprogramok, eljáráshívás

Az Eiffel egy osztály-orientált nyelv, ezért minden eljárás és függvény egy osztály műveleteként adható meg, és mindig egy meghatározott célobjektumra kell vonatkoznia. Híváskor a célobjektumot és az eljárás vagy függvény nevét a pont operátor választja el. Az Eiffel programozási nyelv az osztály-orientáltság következményeként dinamikus kötést használ, az alprogramok egy objektum dinamikus típusa alapján hívódnak. Minden alprogram külön jelzés nélkül is virtuális. Eiffel-ben az alprogramok lehetnek rekurzívak. (Ld. még OOP.)

Az eljárások és függvények megkülönböztetésére nincs külön kulcsszó, ugyanakkor ez a két fogalom erősen elkülönül egymástól. Az eljárásokat állapotváltoztató, a függvényeket lekérdező műveletek megvalósítására használják, vagyis a függvényeknek nem lehet mellékhatása. A nyelv specifikációja ezt nem köti ki, mert lehetnek olyan műveletek, amik változtatják az objektum attribútumait úgy, hogy az objektum kívülről látható (logikai) állapota nem változik (gondoljunk például egy file cache-elt olvasására), és ezt a fordító nem tudná ellenőrizni. Ugyanakkor a műveletek ilyen módon való szétválasztása általánosan elfogadott konvenció, amitől csak nagyon indokolt esetben szokás eltérni.

Eiffel-ben az alprogramok kétfélék lehetnek: a deferred törzsű alprogramok a más nyelvekben absztraktnak nevezett metódusoknak felelnek meg. Ha egy osztály tartalmaz deferred metódust, akkor az egész osztály deferred, tehát absztrakt. A nem deferred alprogramokat effektívnek nevezzük. Az effektív metódusok csoportján belül is kétféle alprogram lehetséges: az external kulcsszóval jelölt, más programnyelven megvalósított eljárás, illetve az internal, amit Eiffel nyelven valósítunk meg.

Példák:

move(mice : MOUSE; men : MENU) is -- Move mouse cursor to first item in menu require mice.move(men) deferred end -- move open_file(file_od : INTEGER; mode : CHARACTER) is -- Open file_od in mode. require file_status(file_od) <= 0 external "C" end; -- open_file file_status(file_od : INTEGER) : INTEGER is -- Current status of file associated with file_od external "C" alias "_fstat" end -- file_status

Egy alprogram specifikációjához hozzátartozik az elő- és utófeltétel megadása is (a deferred alprogramoknál is). Az előfeltételt require kulcsszó után, míg az utófeltételt az ensure kulcsszót követően adhatjuk meg. A feltételek egyszerű logikai kifejezések lehetnek, melyek futás közben ellenőrzés alatt állnak. (Ld. még helyességbizonyítás.)

Egy alprogramban a local kulcsszó után adhatók meg a metódus belső, lokális munkaváltozói. Ezek kizárólag csak az alprogram törzsén belül használhatók, azon kívül érvényüket vesztik.

Egy alprogram végén rescue kulcsszó után állhat a kivételkezelő rész. Megadása nem kötelező, de ajánlott. (Ld. még kivételkezelés.)

Példa:

imp_create(w : PIXMAP) is -- Create pixmap structure for w. require pixmap_exists : w /= Void local color : INTEGER do color := White_color; e_ref := w; imp_reference := c_createpixmap(w.width, w.height, imp_depth, color) ensure imp_reference /= 0; e_ref = w rescue default_rescue end -- imp_create

Egy belső alprogram törzse kétféle lehet: ha do kulcsszóval kezdődik, akkor a törzs az alprogram minden hívásakor lefut. Ha once kulcsszóval kezdjük az alprogram törzsét, akkor az az adott objektum élettartama során legfeljebb egyszer fog lefutni, az alprogram legelső hívásakor; a későbbi hívások mindegyike hatástalan lesz. Ezzel a módszerrel lehet Eiffel-ben összetett típusokból konstans objektumot készíteni.

Példa:

shared : SOME_REFERENCE_TYPE is -- A reference to an object shared by all -- instances of the enclosing class once !!Result ... (...); -- Operations on Result, -- to initialize the shared object ... end -- shared

Egy osztály definíciójában a creation kulcsszó után felsorolt eljárások speciális szerepet töltenek be: ezek lesznek az osztály konstruktorai. A konstruktorok nevére nincs határozott megkötés (mint pl. C++ -ban), a nyelv fejlesztői által ajánlott név a make.
A nyelv nem engedi meg, hogy egy osztály két rutinjának azonos legyen a neve, azaz nem támogatja a függvénynév túlterhelést. Különböző osztályoknak lehetnek azonos nevű rutinjai, ilyenkor a megfelelő rutin kiválasztása a célobjektum dinamikus típusa alapján történik. Például megengedett a következő:

local w :WINDOW; f :FILE; do ... f.open; w.open; end

Ugyanakkor nem megengedett a következő:

class TEXT_FILE feature put(i : INTEGER) is ... end put(c: CHARACTER) is ... end end -- TEXT_FILE

Erre megoldást jelenthet, ha az alprogram nevében is jelezzük paraméter típusát: pl. put_int, put_char.

Paraméterátadás

A nyelv szigorú típusossága az alprogramokra is vonatkozik, így paraméterátadáskor a aktuális paraméterek számának és típusának pontosan meg kell egyeznie az alprogram szignatúrájában szereplőkkel. A paramétereket csak pozícionálisan lehet megadni, nincs lehetőség default értékek megadására sem.Változó argumentumszámú alprogramot a tömb típus segítségével valósíthatunk meg.

Példa:

write(values : ARRAY[NUMERIC]; format : STRING) is -- Print all the elements of values, under the given format ... Procedure body omitted ... -- hívás: c.write(<>, output_format)

A paraméterek neve nem lehet azonos valamely attribútum nevével. Ebben az Eiffel eltér a legtöbb objektum-orientált nyelvtől, ahol a paraméterek névegyezés esetén általában eltakarják az azonos nevű attribútumot. A névütközés más nyelvekben rendszerint minősített hivatkozással oldható fel. Eiffelben egy másik nevet kell kitalálni a paraméterváltozóknak, ami a konstruktor eljárások írásakor néha kényelmetlen.

A paraméterátadás úgy történik, mintha a formális paramétereknek megfelelően lokális változók jönnének létre, és ezután mindegyik értékül kapná az aktuális paraméterként átadott kifejezések értékét. Az értékadás szabályai alapján ebből az következik, hogy hagyományos fogalmakat használva a paraméterátadás röviden így jellemezhető: ha egy formális és a hozzá tartozó aktuális paraméter is referencia típusú, akkor cím szerinti paraméterátadás történik, minden más esetben érték szerinti. Másfajta paraméterátadásra nincs lehetőség. Az expanded típusok esetében a lehetőségek még korlátozottabbak, mert csak az érték szerinti paraméterátadás használható. A paraméterek értékét a rutinok nem változtathatják meg (persze ha egy rutin paraméterként egy referenciát kap, akkor megváltoztathatja annak az objektumnak az állapotát, amire a referencia mutat, de a referenciát nem állíthatja máshova).

Az aktuális objektumra Current néven lehet a rutinokban hivatkozni, ez is persze csak olvasható. Függvények esetén a függvény eredményét a Result nevű változóba kell beleírni, ami a függvény egy implicit módon definiált változója, melynek típusa megegyezik a függvény visszatérési értékének típusával, egyébként pedig ugyanúgy viselkedik, mint a többi lokális változó. Az Eiffelben nincs return utasítás, a függvény akkor fejeződik be, amikor a vezérlés a végére ér, eredménye pedig a Result aktuális értéke.

Példa:

fill_style_count : INTEGER is -- Number of defined fill styles for this figure do Result := global_fill_style_count + local_fill_style_count end -- fill_style_count