Ahogy már a nyelv részletes leírásánál említettük, Scalaban minden objektum. Nézzünk egy ezt bemutató példát:
Minden osztály ősosztálya a nyelvben a scala.Any osztály, két közvetlen leszármazottal: a scala.AnyVal -lal, illetve a scala.AnyRef -fel; az elöbbi az értékosztályokat, míg az utóbbi a referenciaosztályokat reprezentálja. Az osztályhierarchia alján a scala.Nothing áll. A Javas java.lang.Object megfelelője itt a scala.AnyRef.
A program működése egyszerű: létrehoz egy olyan generikus halmazt, aminek a típusparamétere Any, azaz bármit tartalmazhat. Ezek után hozzáadjuk a különböző típusoknak megfelelő értékeket illetve változókat, majd végigmenve a halmazok elemeink, kiiratjuk azok toString() metódusát.
A program kimenete a következő:
Ahogy a nyelv részletes leírásában már említettük, a nyelv nem definiál kifejezett operátorokat, tetszőleges literál betöltheti ezt a szerepet (ésszerű keretek között). Nézzünk egy példát, amelyben megvalósíjuk a komplex számok kezelését, majd az így bevezetett operátorokat gond nélkül felhasználjuk:
Definiáljuk az osztályt, és a rajta végezhető műveleteket, felülírjuk a toString metódust, majd kiszámoljuk egy egyszerű kifejezés értékét.
A Scalaban beépített mintaillesztési lehetőség található, amely szinte bármilyen adatra alkalmazható; a feldolgozás a legelső mintaegyezésig történik meg.
A fenti példában a matchTest() egy olyan metódus, ami a paramétert elösször összeveti az 1 -el, mint egész számmal, és ha egyezik, a "one" karakterlánccal tér vissza. Amennyiben a paraméter a "two" karakterlánc, úgy viszont a visszatérési érték a 2 egész szám. A harmadik illesztés úgynevezett típusos illesztés, ami tetszőleges Int típusú paraméterre illeszkedik, és ez esetben a "scala.Int" karakterlánccal tér vissza.
A Scala 2.9 -es kiadásának egyik nagy ujdonsága volt, hogy az alapnyelv részévé tette a másik fejezetben már tárgyalt párhuzamos feldolgozást a beépített tárolókra. Nézzünk egy példát!
A fenti példában az isPrime() egy olyan metódus, ami eldönti egy számról, hogy prim-e. Azért választottuk ezt a naív implementációt, hogy be tudjuk mutatni a szekvenciális és párhuzamos végrehajtásból adódó különbségeket. A numOfPrimes() metódus megmondja, hogy egy adott felső határ alatt hány darab prímszám található. Ha a fenti programot lefuttatjuk, a futásidő egy 24 magos Intel Xeon E7530 processzoros szerveren nagyjából 20 másodperc (5 futás átlagolt értéke). Annak, hogy közvetlen a nyelvbe építették be a párhuzamos feldolgozást, egy következménye, hogy nagyon kényelmes használni. Ha a legutolsó sort lecseréljük erre a sorra:
A program máris párhuzamosan fogja kiértékelni a map metódust (ugyanez igaz az összes, eddig szekvenciális adatfeldolgozást használó metódusra. További információk erről a bejelentésben. Az így átalakított kód futásideje jóval jobb, 10 másodperc, ami 100%os sebességnövelést jelent.
Scalaban lehetőség van úgynevezett case osztályok létrehozására. Ezen osztályok explicit léteznek, nem kell őket a new operátorral létrehozni. A többi különbség a normális osztályokhoz képest itt olvasható. Ezeknek az osztályoknak a legfőbb vonzereje az, hogy mintailleszthetünk rajtuk.
Nézzünk meg egy konkrét példát, amiben egy kitalált játéknyelv szintaxisfáját akarjuk feldolgozni (és ehez általánosított algebrai típusokat használunk):
Az alap term a fenti példa alapján lehet integer-literál, egy szam rákövetkezője, egy if kifejezés, illetve egy ellenőrzés, hogy egy term nulla-e. Az eval metódus mintaillesztés felhasználásával ki tudja értékelni a kapott termet. A program futtatásakor az eredmény 42, ugyanis az 1 integer-literál nem nulla, így a Succ(Lit(41)) lesz a kiértékelt érték.
Első példaként lássuk a gyorsrendezést Scala-ban:
Ránézésre ez nagyon hasonlít a Java-ban vagy C-ben írt rendezésre. Ugyanazokat az operátorokat és hasonló struktúrákat használunk, azonban van néhány fontos különbség:
Mint látható, Scala-ban lehetőség van "egyszerű" imperatív vagy objektum-orientált programok írására. Azonban lehet funkcionális stílusban is programokat írni:
Ez a megoldás sokkal jobban tükrözi a gyorsrendezés alapötletét:
Mint látható az imperatív implementáció az argumentumként kapott tömbön dolgozik, a funkcionális változat egy
teljesen új tömböt állít elő.
A fenti példában az Array[t] osztály metódusait használtuk (filter, append, sort,
...).
Ebben az osztályban van például egy filter metódus, mely paraméterként egy predikációs függvényt kap, ami a
tömb elemeihez logikai értékeket rendel. A filter alkalmazásának eredménye egy olyan tömb, mely azokat az elemeket
tartalmazza az eredeti tömbből, melyekre a a függvény igazat ad. Vagyis egy Array[t] típusú objektum filter
metódusának a szignatúrája:
Itt a (t)Boolean olyan függvénytípus, amely egy t típusú elemet kap és logikai értéket ad vissza. A filterhez hasonló
olyan függvényeket, melyek egy másik függvényt kapnak paraméterül, vagy egy függvényt adnak vissza, magasabb-rendű
függvényeknek nevezzük.
A gyorsrendezés esetében a filtert háromszor alkalmaztuk egy-egy névtelen függvény
argumentummal. Az első argumentum azt a függvényt reprezentálja, ami a paraméterét a megfelelő összehasonlítás
értékére képezi le. Az x típusát elhagytuk, mert a fordító képes arra, hogy meghatározza azt.
Természetesen a
filter-t és a hasonló függvényeket névvel ellátott függvényekre is alkalmazhatjuk:
Egy Array[t] típusú objektumnak van append metódusa is, mely egy másik tömböt kap paraméterként és visszaadja a két tömb összefűzését. Ennek a szignatúrája:
Ezt mi a fenti példában, mint infix operátort alkalmaztuk. Valójában minden metódus operátorként is alkalmazható a Scala-ban. Egy E op E' bináris operátor megfelel egy E.op(E') metódushívásnak. Tehát a fenti esettel ekvivalens a következő:
A „hagyományos” operátorok is az appendhez hasonlóan kezelhetőek. Például az i + 1 kifejezés ekvivalens az i.+(1)
metódushívással, ahol a + az x egész érték metódusa.
A vezérlési szerkezetek is előredefiniált függvények.
Például a while:
Ennek első paramétere egy teszt függvény, második paramétere pedig egy akció függvény. A while addig hívja az akció függvényt, amíg a teszt függvény igaz értéket ad vissza.
Jöjjön egy példa, mely mutat egy olyan területet, ahol a Scala különösen jól alkalmazható. Tegyük fel, hogy egy
elektronikus aukciót kell implementálnunk. Ehhez aktorokat használunk, vagyis olyan objektumokat, amelyek üzenetek
fogadására képesek. Minden folyamatnak van egy levelesládája, melyben a bejövő üzenetek vannak (sor). A
levelesládából nem csak sorban lehet kivenni a leveleket, hanem adott tulajdonság alapján is.
Visszatérve a
példához legyen egy árverező folyamatunk, amely egy adott áruról információkat szolgáltat, fogadja az ajánlatokat a
kliensektől és biztosítja a kommunikációt az eladó és a győztes ajánló között az aukció végén.
Először is
határozzuk meg a lehetséges üzeneteket. Két alaposztályunk van aszerint, hogy az üzenet honnan hova megy. Az
AuctionMessage üzenetek a kliensektől mennek az árverező felé, az AuctionReply üzenetek pedig vissza.
Az árverező folyamat egy lehetséges implementációja a következő:
Az objektum létrehozásakor meg kell adni:
A folyamat működése a run metódusában van definiálva. Ez mindig kiválaszt egy üzenetet a receiveWithin metódussal, és arra reagál, egészen addig, míg a megadott idő le nem jár. Az utóbbi esetet a TIMEOUT üzenet jelzi. Terminálás előtt a folyamat még egy ideig fogadja az üzeneteket, és azokra válaszol. Néhány további magyarázat a programhoz:
A fentiekből úgy tűnhet, hogy a Scala-ban igen sok, az elosztott programozást támogató nyelvi konstrukció van. Valójában arról van szó, hogy az Actor osztály metódusait használtuk.
Ebben a példaprogramban egy aktorokkal megvalósított párhuzamos rendszert mutatunk be és lehetőség nyílik
a Scala gyakorlatias szemléletű megismerésére.
Készítette: Sárkány András 2011
Fordítóprogram: Scala 2.9.0
Fejlesztőkörnyezet: Eclipse
A program szupermarketben vásároló és fizető vevőket (Customer) szimulál, ahol a legtöbb dolog párhuzamosan történik. A vevők kiválasztják azokat a termékeket amiket meg akarnak vásárolni, majd véletlenszerűen beállnak egy kassza előtti sorba, amikor sorra kerülnek a vásárolt áruk árát a pénztáros kikéri a nyílvántartásból, majd a vevő fizet és távozik.
Három különböző típusú Aktorral és egy statikus Singleton objektummal oldjuk meg a feladatot.
A Customer aktor testesíti meg a vásárlót, aki miután bevásárolt kiválaszt egy pénztárat (Counter) és beáll a neki megfelelő sor(Line) végére. Mivel mind Aktorok ezért üzenetekkel kommunikálnak, tehát például a Customer üzen a Line-nak, hogy ő most beáll a sor végére, a Counter pedig üzen a Line-nak ha jöhet oda egy ember mert épp távozott valaki. A Supermarket mint globális objektum adja a szimuláció környezetét, ő kezeli a nyílvántartást és hozzá tartoznak a pénztárak is
Nézzük először a Supermarket forráskódját.
Az assignNextId() függvény visszaadja a soron következő azonosítót és elvégzi a léptetést is. Szépen látszik a különleges Scala szintaxis, ami egyszerre imperatív és funkcionális: ha több utasítást írunk egymás után akkor azok szekvenciaként hajtódnak végre és az utolsó lesz a blokk visszatérési értéke
Ebben a kódrészletben az áruház alá tartozó pénztárak kezelését láthatjuk. Az initCounters függvény hatására létrejönnek a pénztár objektumok egy listában (counters), a startCounter függvény hatására pedig elindulnak a pénztárosok futó szálai
Ez a buy metódus szimulálja a bevásárlókocsi megpakolását. Mindenki véletlenszámú árut vesz (0-10) és minden áru kap egy véletlenszerű árat (0-100). Az így generált áru aztán bekerül az "adatbázisba". Ez a függvény visszadja a kiválasztott áruk listáját, melyet a változatosság kedvéért tömbbel implementáltunk. A függvény visszatérési értéke az utolsó sorban található "a", vagyis a tömb maga
Nézzük meg milyen üzenetváltásokra számítunk:
Ha egy Customer befejezte a válogatást választ véletlenszerűen egy sort és küld neki egy üzenetet, jelezve, hogy
beállt a végére(StandInLine).
A sor elvégzi ennek az akciónak a nyílvántartását. Ha egy kassza megüresedik, akkor küldd egy üzenetet a hozzá
tartozó sornak, miszerint jöhet valaki(NextPlease). Ekkor a sor rögtön jelez a vásárló felé, hogy a kassza szabaddá
vált (YouAreNext).
A kassza és a vásárló közti kommunikáció egy párbeszédként írható le: a vásárló jelzi hogy jelen van (ImHere), mire a
kassza kéri
a vásárolandó elemeket (GiveMeYourItems). A vásárló ezeket odaadja (Items), mire a kassza mond egy összeget (Price).
A vásárló ezután kifizeti az összeget(Money) és távozik
A forráskód
A vásárló saját bevásárlókosarának tartalmát az items tömbben tárolja. A vásárlás szimulálására meghívja a szupermarket buy függvényét. Az Aktor élete kezdetén szimulálja a vásárlást (feltölti a kosarat), kér egy véletlenszerű kasszát és beáll az ő sorába. Amikor sorra kerül lefolytatja a párbeszédet a kasszával és távozik
A forráskód
A Counter létrehozza a saját sorát és mivel az is egy külön futó aktor, el is indítja. Ezután jelzi a sornak, hogy nincs nála vevő és vár a további üzenetekre. Amikor megkapja a vásárolt elemek listáját (Items(items)) kiszámolja a teljes vásárlás összegét. Felhívjuk a figyelmet, hogy a vásárlónál lévő áruk nem tárolják az árukat, ezt a központi raktárból kell lekérdezni.
Ezt a sort elemezzük egy kicsit jobban. A belső for kifejezés minden i item-re lekéri az árát, és a yield segítségével ebből előáll egy új lista, ami az árakat tartalmazza már. Ennek az új listának a sum függvénye kerül meghívásra, és az összeg a Price konstruktorparamétere lesz, majd üzenetben távozik.
Végül nézzük meg a várakozási sort megvalósító Line aktort
A Line osztály tárolja a várakozó vásárlók listáját (inline), valamint egy külön logikai
értéket arra az esetre, ha nincs senki a kasszánál. Erre azért van szükség mert ha akkor jelzi egy kassza,
hogy üres amikor a várakozási sor is üres, akkor egy újonnan érkező vásárló magától nem tudná,
hogy mehet a kasszához, a kassza meg nem jelezne újra.
Kétfajta üzenetet fogad az osztály: StandInLine és
NextPlease.
A StandInLine-t biztosan vásárlótól kaptuk ezért cast-oljuk a küldő objektumot és betesszük a sor végére.
Ha nincs senki a kasszánál saját magunknak küldünk egy üzenetet amivel kiváltjuk az újonnan érkezett vevő
áthaladását a kasszához.
Ha NextPlease üzenet érkezik, akkor a kassza készen áll egy vásárló fogadására. Mintaillesztéssel leválasztjuk a lista fejét, és a (volt) fejelemnek elküldjük a YouAreNext üzenetet. Ha nem tudjuk a fej-farok mintaillesztéssel szétválasztani a listát akkor üres, ekkor beállítjuk a flag-et a megfelelő értékre.
A teljes forráskód letölthető itt. Ez a példa jól rámutat arra, hogy a Scala aktorainak üzenetküldése nagyon hasonlít az egyzserű függvényhíváshoz. Egyes tervezési döntések meghozatalakor (például a Line osztály esetében) végig érezhető, hogy egy másik implementációban, külön futási szál nélkül a Counter osztály részeként kezelhető lenne. A funkcionális paradigmával rengeteg kódrészlet rövidíthető le pársorosra és az immutable elemek használatával a programozás nagyobbrészt fordítási idejű és csak kisebbrészt futásidejű problémák megoldásává alakul.
A Scala nyelv elosztott programok készítésére nyújtott magasszintű eszközeinek szemléltetésére egy kliens-szerver felépítésű programot válaszottam. A programban egy bankár szerverként fut, hálózaton elérhető, kliensektől hiteligyénylések érkeznek hozzá. A bankár bizonyos feltételek mentén eldönti, hogy elfogadja-e az igénylést, majd ennek megfelelően vagy elutasítja az igényt, vagy megkötik a hitelszerződést, majd rögtön meg is kezdi az ügyfélnek a folyósítást. A bankár adott cikluson ként kéri az ügyfeleit a törlesztő befizetésére, majd a futamidő leteltével felbontják a kapcsolatot.
A bankár egy aktorral van megvalósítva. Egy bankár létrehozásához meg kell adnunk, milyen indulótőkével rendelkezik. Ez az, amiből tud majd az ügyfeleknek hitelt adni. Egy korlát meg van adva, hogy mekkora részét adhatja ki tőkéjének maximálisan. Ezt hiteligénylések elbírálásánál figyelembe fogja venni az aktuális tőkére. Ezenkívül meg kell adni, milyen porton akarjuk majd a bankár programot elindítani. A külvilág számára az adott hálózaton, ezen a porton lesz elérhető.
A bankár viselkedését az act metódusban a következőképp írhatjuk le.
Egy hiteligény bejelentését jelző üzenet. Tartalmazza az igénylő ügyfelet, valamint az igényelt összeget.
Az igénylés elutasítása esetén küldött üzenet.
Törlesztés teljesítését jelző üzenet.
Törlesztésre figyelmeztető üzenet.
Hiteligénylés esetén a megadott hitel adatait tartalmazó üzenet. Leírja a futamidőt, kamatot, a felvett összeget, a lefoglalt tulajdont, a törlesztésből hátralevő időt, a törlesztőrészlet összegét, az ügyfél azonosítóját.
Az ügyfél logikai működését megvalósító osztály. Tartalmazza adott összegű igénylés elindítását, törlesztőrészlet fizetését, az igényelt összeget, az egyedi ügyfélazonosítót, és elfogadott igénylés esetén a felvett hitelt.
A kliens kommunikációs részét megvalósító osztály, ami egy aktorba „csomagolt” Client objektumot jelent. A
kommunikáció szintén az act metódusban van leírva. Induláskor meg kell adni a bankár elérhetőségét (hálózaton),
amelyiktől hitelt akar igényelni, valamint meg kell adni az ügyfél adatait: fizetést, a vagyont, amire a hitelt fel
akarja venni, életkorát.
Hitel igénylésekor történik a kliens aktor tényleges elindítása, amely első lépésként szintén figyelni kezd egy
megadott porton, majd felveszi a kapcsolatot a bankár szerverrel és elküld neki egy üzenetet (Application) a
hiteligénylésről. Elutasítás esetén abbahagyja működését, elfogadás esetén rögzíti magának a hitel adatait, a
megjelölt tulajdona lefoglalásra kerül. A bankár által jelzett időben mindig fizeti az aktuális törlesztőt, a
futamidő lejártával pedig befejezi működését.
A kliens által birtokolható vagyon absztrakt ősosztálya. Tartalmazza a tulajdonos klienst, van egy „reál-értéke”, meg van adva, hogy legfeljebb mekkora részét fedheti a hitel az értékének, mekkora kamata lesz az erre felvett hitelnek, maximálisan mekkora futamidejű hitelt lehet rá felvenni. Egy logikai érték jelzi, hogy le van-e már foglalva egy hitellel.
Lehetséges megvalósítások: ingatlan, arany, gyémánt, autó, értékpapír.
Mikor egy ügyfél elküldi az igénylési üzenetét a bankárnak az feldolgozza a kérelmet. Ez a következő feltételek mentén történik: a bankár tőkéjének egy megadott százalékát nem haladhatja meg az igényelt összeg; fedezi-e az ügyfél tulajdonának értéke az összeget; nincs-e már lefoglalva a tulajdon. Ha ezek teljesülnek, kiszámtja a maximális futamidőt, a törlesztőrészletet és megvizsgálja, hogy ez nem haladja-e meg az ügyfél fizetésének harmadát. Ennek megfelelően vagy elutasítja a kérelmet, vagy megkezdi a folyósítást.
A Scala funkcionális nyelvi elemeit szándékoztam bemutatni egy mélységi keresés funkcionálisan megírt implementációjával. A main függvény az ami egyedül imperatívan van megírva.
A main függvény pusztán az IO kezelésre és a mélységi keresés elindítására szolgál, illetve a mélységi bejárás által
eredményezett mélységi számok szerint itt lesznek sorba rendezve a csúcsok.
A program egyetlen paramétert fogad el, amelynek soronként egy csúcsnév párt kell tartalmaznia. Például így:
Ez az osztály tartalmazza a gráf reprezentációját miszerint a csúcsok szomszédjainak listáit egy vektorban tároljuk, egy másik vektorban pedig a csúcsok címkéit. Ami érdekes lehet az a külső konstruktor megoldása: Scalában a default konstruktort nem lehet akárhogy túlterhelni, ezért egy az osztállyal megegyező nevű singletonnal és annak apply() metódusával kellett egy külső konstruktort mímelni (bővebben ld. forrásban a kommenteket).
A mélységi bejárás funkcionális implementációja, bőségesen ellátva kommentekkel. Értelemszerűen a ciklusokat ki kellett váltani rekurziókkal, így összesen három rekurzív függvény hívogatja egymást, ami nem túl szép, de működik.
Egy egyszerű, egy gépen, két játékossal játszható amőba játék.
A következő példaprogramban adatbázis lekérdezéseket végzünk (az Oracle XE mintaadatbázis HR sémáját felhasználva), hogy szemléltessük, hogyan dolgoznak magasabb rendű vezérlő struktúrák, valamint példát látunk case osztályokra, amelyeken mintaillesztést végzünk. Első lépésben definiálunk egy using metódust mely két paramétert vár. Ezeknek típusai A és B. B típusú paraméternek nincs semmilyen megkötése, viszont A típus paraméter megszorítására a strukturális típusparaméter módszerét alkalmazzuk (duck typing). Ezzel a módszerrel megkövetelhetjük, hogy az első paraméter olyan típus legyen, ami megvalósít egy close() nevű eljárást. A második paraméter egy függvény, amely A típust leképezi B típusúra, mivel Scala-ban a try szerkezet visszaadja a blokkban található kifejezést, így esetünkben a függvényünk visszatérési értékét a try blokkban megkapjuk. A finally ág biztosítja, hogy param argumentum mindenkor lezárásra kerüljön.
Nézzünk példát a metódus használatára:
A következő vezérlő utasításban készítünk egy ciklust, amely addig fut, amíg a test változónk igaz. A ciklusmag minden végrehajtásakor a metódus összegyűjti a kimenetre az átadott név értékét és bővít vele egy listát. Ehhez deklarálunk egy bmap (Boolean Map) nevű metódust. Ez a metódus két paramétert vár: egy logikai függvényt test néven, a második paraméter egy kódblokk, amely leképez egy T típusú objektumot és hozzáfűzi a ret-hez. A függvény végül visszaadja a listát.
Ezek után definiáljuk a case osztályainkat, amelyek egy közös absztrakt őse van, az Employee. A case osztályok használata nagyban leegyszerűsíti a saját adattípusok (osztályok) írását, hiszen a fontosabb metódusokat és konstruktort implicit kapunk hozzájuk. A példában két típusra bontottuk a dolgozókat (vezetőség és dolgozók), hogy később mintaillesztést végezhessünk rajtuk.
A findPeopleExecutive függvény paraméterként megkapja a Connection String-et. Ebből tudja, hogy melyik adatbázishoz, milyen driverrel, melyik felhasználó nevében azonosítva kell belépve lekérdezést végrehajtva Employee típusú listát előállítva visszaadni. A külső using() függvény paramétere a végrehajtandó SQL utasítást reprezentáló objektum. Ehhez hozzárendeli a belső using() függvény által visszaadott értéket. A belső using() függvény végrehajtja az SQL lekérdező utasítást, amely egy kétoszlopos eredménytáblát ad vissza, kiválogatva a minta-adatbázisból az emberek összekonkatentált nevét és fizetését. Ezt az eredménytáblát röptében feldolgozva, lefuttatja rajta a bmap() függvényt, amely bejárja és feldolgozza az eredménytáblát. Amíg az rs.next() teszt feltétel igaz, addig az eredménytábla rekordjaiból egyesével példányosít egy-egy Executive osztályú objektumot, amelyeket végül listává konvertálva visszaad. A dolgozókat összegyűjtő függvényben csak az SQL lekérdezés különbözik.
A példaprogram utolsó függvénye a mintaillesztést mutatja be, amely a kiválogatás programozási tételt valósítja meg, ahol egy sorozatból feltételtől függően egy kiválogatott sorozatot kapunk vissza. A mintaillesztésben a dolgozók osztályaira és azon belül a fizetésre, mint attribútum értékre is illeszthető a minta. Mivel nem minden objektum illeszthető, ezért alkalmazni kell, az _ joker mintát különben kivétel keletkezik.
A Scala nyelv speciális lehetőséget kínál xml adatok feldolgozására úgy, mint xml fájlok beolvasása, vagy mentése, lekérdezés xml fájlokból, valamint xml adatokon mintaillesztést is végezhetünk. Scala-ban az xml fájlokat a scala.xml.Elem osztály definiálja. Létrehozhatunk xml adatot literálként is, ha a kifejezés érvényes xml.
<a>
This is some XML.</a>
Kapcsos zárójelek között Scala forráskódot is elhelyezhetünk.
<bait>
{ if (yearMade < 2000) <old>
{yearMade}</old>
else xml.NodeSeq.Empty }
</bait>
Ezek után könnyedén hozhatunk létre például egy toXml függvényt egy osztályban, ha visszatérési értéknek xml literált adunk meg és a megfelelő xml tag-ek között kapcsos zárójelekkel megadjuk az adattagokat.
<cctherm>
<description>
{description}</description>
<yearMade>
{yearMade}</yearMade>
<bookPrice>
{bookPrice}</bookPrice>
</cctherm>
}
Xml adatok feldolgozására Xpath lekérdezőnyelven alapuló metódusokat használhatunk, amelyekkel könnyedén tudunk deszerializálni xml adatokat (egy apró különbség a Xpath / és // jeleitől, hogy Scala-ban a \ és \\ jelet kell használni a megjegyzések miatt).
Az XML osztály save metódusával tudunk menteni xml állományt, illetve a loadFile függvénnyel betölteni.
Xml adatokon a mintaillesztést is használhatunk, ahol is az xml tag-eket használhatjuk fel az illesztésre. A behelyettesítendő mintát blokk között kell megadni hasonlóan az xml literálokhoz.
<a>
{contents}</a>
=> "It's an a: "+ contents
case <b>
{contents}</b>
=> "It's a b: "+ contents
case _ => "It's something else."
}
A következő néhány sor egy egyszerű megoldást nyújt egy nem is olyan egyszerű problémára. Egy NxN-es sakktáblán
szeretnénk elhelyezni N db királynőt úgy, hogy azok ne üssék egymást. Ez az N királynő probléma, amely a
8 királynő probléma általánosítása.
Ebben a modellben a királynőket koordinátapárokkal jelöljük, ami egyértelműen meghatározza egy adott királynő
pozícióját a táblán. (a koordináták 1 és N közé esnek)
A queens(n) függvény egyetlen paramétere a sakktábla mérete (ami egyben a királynők száma is), visszatérési
értéke pedig a megoldások listája. Ennek a listának minden eleme egy számpárokból álló lista, amely leírja mely
koordinátájú mezőkre kell állítanunk a királynőket. Magát a problémát ez az egy függvény oldja meg, de ehhez szükség
van a következő két segédfüggvényre is.
Az isSafe(newQueen, queens) függvény newQueen paramétere az újonnan a táblára helyezendő királynő
koordinátáit jelölő számpár, queens paramétere pedig a táblán már elhelyezett királynők koordinátáinak
listája. A függvénynek true a visszatérési értéke, ha a táblán már elhelyezett királynőket figyelembe véve az
újonnan felhelyezendő királynő biztonságban van, azaz egyetlen királynő sem üti azt, minden más esetben false
a visszatérési értéke.
Az inCheck(queenA, queenB) függvény queenA és queenB paraméterei egy-egy királynő
koordinátái. Ez a függvény is egy logikai értékkel tér vissza attól függően, hogy a paraméterként kapott királynők
ütik egymást vagy sem.
A fenti megoldás egy egyszerű trial-and-error módszert valósít meg, oszlopról oszlopra haladva próbál felhelyezni egy újabb királynőt és ha sikerült felhelyezni N darabot, akkor ezt tekinti egy lehetséges megoldásnak, majd tovább próbálkozik. Ha a megoldás egyszerűségét szembeállítjuk a probléma bonyolultságával, jól látszik mi mindenre képes a Scala nyelv.
A következő program egy egyszerű számológépet mutat be, amelyben az alapvető műveletek végezhetőek el (összeadás,
kivonás, osztás, szorzás, gyökvonás és reciprok műveletek). A feladat megoldásához és megjelenítéséhez a scala.swing
csomag nyújtott segítséget. Az alkalmazás a SimpleSwingApplication
osztályra épül.
Maga a feület egy szövegmezőből és gombokból épül fel. A megjelenítést elrendezéséről a GridBagPanel
gondoskodik. Az egyes elemeket ezen a felületen úgynevezett megkötésekkel lehet elhelyezni:
A felületre a gombokat egy gyártó metódussal generáljuk. Ez a metódus az összes gombnak egységesen beállítja a betűméretét és az egy gridre eső méretét. A programban ez a metódus a következőképpen néz ki:
A program tartalmaz egy szövegmezőt is, ami tulajdonképpen a számológépnek a megjelenítője. A szövegmező csak egyszer kerül létrehozásra, úgy hogy ne lehessen szerkeszteni. A szövegmezőt a következő kód generálja ki:
A teljes számológép programotot a itt lehet letölteni.
A program a népszerű 2048-as játék játszására nyújt lehetőséget.
A játék lényege, hogy egy 4x4-es négyzetben kell a számokat úgy tologatni, hogy a 2 hatványai végül 2048-at adjanak ki. Az egész hálót mozgatjuk négy irányba, a mozgatásokkal az azonos értékek összecsúsznak és összeadódnak.
Készítette: Balogh Bernadett, 2014
Fordítóprogram: Scala 2.10.3
A forrás itt található. Klasszikus 3x3-as amőba játék, konzolban történő megjelenítéssel és irányítással, 2 emberi játékos részére. Forrás: letöltésAmőba - konzol