Függvények
A Dylan nyelvben minden művelet egy függvény. A függvényeknek nulla vagy több argumentumuk és nulla vagy több visszatérési értékük van. A függvény paraméterlistája megadja a paraméterek és a visszatérési értékek számát és típusát.
A függvényeknek két csoportja van, a metódus (method) és a generikus függvény (generic function). Mindegyiket ugyanúgy használjuk, a függvény hívójának nem is kell tudnia, hogy metódust vagy generikus függvényt hívott. A metódus a futtatható kód egy egysége. A metódus fogadhat paramétereket, amelyekhez lokális neveket rendel. Lefuttatja a törzset ezeknek a lokális neveknek a terében és visszatérhet valamilyen értékekkel.
A generikus függvény metódusokat foglal magába. Amikor egy generikus függvényt meghívunk összehasonlítja az argumentumait azoknak a metódusoknak a paraméterlistáival, amelyeket tartalmaz. A paraméterlista alapján kiválasztja a megfelelő metódust és átadja neki az argumentumokat. A generikus függvény a polimorfizmust megvalósító eszköz a Dylan nyelvben.
A Dylanban minden függvény objektum, méghozzá a funcion példánya. Ezen kívül a metódusok példányai a method-nak, a generikus függvények pedig a generic-function-nak.
Generikus függvények
Generikus függvényeket kétféleképp hozhatunk létre: a define generic segítségével, vagy pedig a make utasítás használatával. A make utasítás segítségével hozhatjuk létre egy típus egy példányát, tehát a második esetben a make utasítást a típusra kell meghívni.
A define generic haszbálata azonban jóval gyakoribb.
Ha egy osztályhoz adattagot definiálunk (lásd Objektum orientált programozás) akkor létrejönnek az adattagot beállító és lekérdezo generikus függvények.
A következő példában definiálunk egy generikus függvényt amelynek egy paramétere van.
define generic double()
Ehhez a generikus függvényhez olyan metódusokat adhatunk, amelyeknek egy (kötelező) paraméterük van. A következő példában definiálunk egy olyan generikus függvényt, amelynek két number típusú paramétere van.
define generic average(n1 :: number, n2 :: number)
Metódusok
Metódusokat definiálására három lehetőség van:
- define method: Az így létrehozott metódus egy generikus függvényhez adódik.
- local: Így definiálhatunk rekurziót tartalmazó metódust.
- method: Az így létrehozott metódust közvetlenül használhatjuk, átadhatjuk más metódusoknak paraméterként, vagy akár el is tárolhatjuk valamilyen adatstruktúrában.
Metódus létrehozása a make utasítással nem lehetséges.
define method
A define method-al létrehozott metódus hozzáadódik a generikus függvényhez. Ha még nem definiáltuk a hozzá tartozó generikus függvényt akkor létrehozza azt. Tehát egy ilyen módon létrehozott metódus vagy létrehoz egy generikus függvényt, vagy hozzáadódik egy már meglévőhöz. Akkor kell a metódusunkat ilyen módon definiálni, ha használni akarjuk a polimorfizmust.
define method double (thing :: number)
=> nother-thing :: number;
thing + thing;
end method;
Ez a metódus egy
típusú argumentumot fogad el, és visszatér a duplájával. A metódus hozzáadódik a double nevű generikus függvényhez, amit feljebb definiáltunk.
Itt hívjuk fel a figyelmet a Dylan nyelv következő tulajdonságára: a nyelvben minden egyes blokknak van értéke. Ha nem adunk meg mást, akkor egy blokk értéke a benne szereplő legutolsó kifejezés értéke. Ha definiálunk egy metódust és külön nem adjuk meg a visszatérési értéket, akkor az utolsó kifejezés értékével tér vissza. Tehát a példánkban szereplő metódus a thing + thing
értékével fog visszatérni.
local
A local-al létrehozott metódus neve a blokkon belül lokális lesz. Egy local deklarációval több metódust is létrehozhatunk, ezek a metódusok meg is hívhatják egymást. Az ilyen metódusok rekurziót is tartalmazhatnak.
define method newtons-sqrt (x :: number)
local method sqrtl (guess) // meghivunk egyéb lokális metódusokat
if (close-enough?(guess))guess
else sqrtl (improve (guess)) //rekurzió
end if
end sqrtl,
method close-enough? (guess)abs (guess * guess - x) < .0001
end close-enough?,
method improve (guess)(guess + (x / guess )) / 2
end improve;
sqrtl(1)
end method newtons-sqrt;
A példában a newtons-sqrt metóduson belül (melyet a define method -al hoztunk létre) létrehozunk három lokális metódust. A három metódus egymás után van megadva, vesszővel elválasztva. A három metódus hívhatja egymást vagy akár önmagát is, ahogy azt az sqrtl lokális metódus esetében látjuk.
Ez a három lokális metódus máshonnan nem hívható, csak a newtons-sqrt törzsén belül (mert ott lettek definiálva). Fontos, hogy a fenti példában mindhárom "belső" metódus lokális, de a második és a harmadik estében már nem kellett kiírni a local kulcsszót. (Egy local után több lokális metódust is definiálhatunk).
method
Metódust közvetlenül a method utasítással is létrehozhatunk. Az ilyen metódust eltárolhatjuk egy változóban, és utána felhasználhatjuk függvényparaméterként, vagy eltárolhatjuk egy adatstruktúrában.
define constant square = method (n :: )
n * n;
end method;
Gyakran hasznos, hogy létrehozzunk egy metódust, melyet átadunk egy másik metódusnak. Nézzük meg a következő példát:
sort (person-list,test : method(person1, person2)
person1.age < person2.age end method)
A rendező metódusnak átadunk paraméterként egy emberekből álló listát, és egy metódust amellyel megadjuk a rendezés módját. A fenti példában a rendezés a személyek életkora alapján történik.
Visszatérési érték
A Dylan metódusoknak nincsen normál "kimeneti" paramétere megadva a paraméterlistában, így sokkal rugalmasabbak a visszatérési értékekkel. A metódusok akár több értéket is visszaadhatnak. Ahogyan a paraméterek, ezekenk az értékeknek is lehetnek típusosak, vagy típus nélküliek. Minden visszatérési értéknek kell legyen neve.
A Dylan metódusoknak - ahogyan minden vezérlő szerkezetnek is - van értéke, ami az utolsó kifejezés értéke a törzsben.
define method foo() => sample :: string;
"Sample string."; // return string
end;
define method bar() => my-untyped-value;
if (weekend-day?(today()))
"Let's party!"; // return string
else
make(excuse); // return object
end if;
end method;
define method moby( ) => sample :: string, my-untyped-value;
values( foo(), bar() ); // return both!
end;
define method baz( ) => ( );
let (x,y) = moby( ); // assign both
end;
A blokk utasítások egy csoportja. Mint minden vezérlési szerkezetnek, ennek is van (visszatérési) értéke. Ha erről máshogy nem rendelkezünk, akkor a blokk értéke az utolsó kifejezés értéke:
block ()
1 + 1;
end; // returns 2
A blokkok támogatják a nem lokális kilépést. Ez lehetővé teszi, hogy a blokk bármikor kiléphessen, és opcionálisan adhat visszatérési értéket. Hogy hassználhassuk ezt a lehetőséget, a blokk kezdetét jelző block kulcsszó után meg kell adnunk egy nevet, amit a Dylan egy kilépési függvényhez köt, amit bárhol meghívhatunk a blokkon belülről, vagy a blokkból hívott függvényekből. A következő példában a blokk egyaránt visszatérhet a "Weird!" vagy a "All's well." string -el, attól függően, hogy milyen színű az ég.
block (finished)
if (sky-is-green())
finished("Weird!");
end;
"All's well."
end block;
Különleges argumentumok
Függvényeknél lehetőségünk van különleges paraméterek megadására. A normál paraméterek száma és sorrendjük rögzített, és megadásus kötelező. A Dylan függvényeknél változhat a további paraméterek száma.
#rest
A #rest paraméter gyűlytemények korlátlan számú argumentumok, mint egy sorozat. Például a következő függvénynek egy kötelező paramétere van (view), és tetszőleges további paraméter adható meg (points). A függvény belsejében egy for ciklus fog végiglépkedni ezeken az opcionális argumentumokon.
define method polygon (view :: point, #rest points)
for (p in points)
...
end for;
end method;
A függvényt pedig az alábbi módokon lehet használni:
polygon(myWindow, p1, p2, p3, p4, p5); // Typical usage
#key
A kulcsszó paramétereket a #key segítségével adhatjuk meg, ami igen hasznos, különösen a sok paraméteres függvényeknél, amelyeknek van alapértelmezett értéke. Ez a lehetőség biztosítja, hogy tetszőleges sorrendben adhatsuk meg a paramétereket, vagy akár el is hagyhassuk a megadását, amennyiben van alapértelmezett érték. Ezáltal növekszik a hívó kód olvashatósága.
define method rent-car (customer :: string, // 2 kötelező paraméter
location :: string, // és még 4 opcionális
#key color = white:, // Default color is white
sunroof? = #f, // Default no sunroof
automatic? = #t, // Default automatic shift
days = 3) // Default 3-day rental
...
end method;
A metódust több módon is hívhatjuk. Kulcsszó argumentum nélkül, vagy akár kulcsszavakkal azonosított paraméterekkel is.
rent-car(arnold, dallas, days: 7, sunroof?: #t);
rent-car(betty, dallas, days: 8, color: #"red");
rent-car(colin, vegas); // minden alapértelmezett
A programozónak egy kis szabadsága van a kulcsszó argumentumok specifikálásánál. Tetszőlegesen elhagyhatja az alapértelmezett értékét egy kulcsszónak (ekkor #f lesz). Alapértelmezett érték megadásánál valójában a függvény önmagát hívja, és támaszkodhat a hagyományos paraméterekre, amelyek beléptek a láthatósági mezőbe. A változónevek különbözhetnek a kulcsszó nevektől, ami egy ügyes eszköz a névkonfliktusok megelőzésére.