trait Lista
case class UresLista() extends Lista
case class NemuresLista(head: Int, tail: Lista) extends Lista
Most erre fogunk feldolgozó kódot írni. Funkcionális programozásban nagyon gyakori az algebrai adattípusok mintaillesztéssel való feldolgozása, amire Scalában a match kulcsszó való. Nézzünk példa kódot, ami kiszámolja a lista elemeinek az összegét:
def sum(list: Lista): Int = list match {
case UresLista() => 0
case NemuresLista(h, t) => h + sum(t)
}
println(sum(NemuresLista(1, NemuresLista(4, NemuresLista(2, UresLista())))) ) //prints 7
Mit is látunk itt? Egy értékre való mintaillesztést a match kulcsszó vezet be, ami után (kötelezően) kapcsos zárójelek közt meg kell adnunk "ha illik erre a kifejezésre a minta, akkor ez a kifejezés értéke, különben ha illik ez a minta, akkor ez, különben ha illik ez a minta, akkor ez,... " eseteket, $\mathrm{case~}P\Rightarrow E$ alakban, ahol $P$ egy "minta", $E$ pedig a kifejezés.
Hogy nézhet ki egy minta? Például:
Hogy nézhet ki egy minta? Például:
- lehet egy azonosító. Ez mindenre illeszkedik, rögzíti (bindeli) az azonosítóhoz az illesztett értéket, és a $\Rightarrow$ jobb oldalán ezt az értéket helyettesítjük be az azonosító helyére.
- lehet egy érték. Ez akkor illeszkedik, ha egyenlő az illesztett értékkel.
- lehet egy $C(P_1,\ldots,P_n)$ alakú kifejezés, ahol $C$ egy case class neve, aminek $n$ mezője van. Ez akkor illeszkedik, ha a bejövő érték szintén ennek a $C$ case classnak egy példánya, az első mezője illeszkedik a $P_1$, a második a $P_2$, $\ldots$, az $n$-edik (utolsó) mezője a $P_n$ mintára. Ha minden illeszkedik és az al-mintákban volt azonosító bindelés, akkor ezek mind átmigrálnak a jobb oldalra.
- az első case nem illeszkedik, hiszen az egy $\mathrm{UresLista}$ case class példányra illeszkedne, $list$ pedig egy $\mathrm{NemuresLista}$.
- a második case addig biztosan illeszkedik, hogy $\mathrm{NemuresLista}$ a minta is, meg az érték is, amire illesztjük. Ekkor tehát a minta akkor illeszkedik, ha stimmelnek a mezők is: azaz, ha a $h$ (azonosító) illeszkedik a $3$, a $t$ (azonosító) pedig a $\mathrm{NemuresLista}(2,\mathrm{UresLista}())$ értékre. Ezek illeszkednek, hiszen változók, tehát a match kifejezés értékét úgy kapjuk, hogy a $h+\mathrm{sum}(t)$ kifejezésben $h$ helyébe a $3$, $t$ helyébe pedig az $\mathrm{NemuresLista}(2,\mathrm{UresLista}())$ értéket helyettesítjük és ezt a kapott kifejezést kiértékeljük, tehát: $3+\mathrm{sum}(\mathrm{NemuresLista}(2,\mathrm{UresLista}()))$ értéke.
def vesszok(list: Lista) = {
def vesszokR(list: Lista): String = list match {
case UresLista() => ""
case NemuresLista(head, UresLista()) => head.toString
case NemuresLista(head, tail) => head + "," + vesszokR(tail)
}
"(" + vesszokR(list) + ")"
}
println(vesszok(NemuresLista(3, NemuresLista(7, UresLista()))) ) // prints (3,7)
Ez ha kap egy listát, akkor kiírja kerek zárójelek közt (ezután jön a rekurzív hívás) vesszőkkel elválasztva az elemeit. Itt arra kell figyelni, hogy ha a lista üres, akkor ne írjunk ki semmit, ha egyelemű (vagyis: olyan nemüres lista, melynek a farka üres), akkor csak azt az egy elemet, egyébként pedig a fejét, vessző, rekurzió a farkára.
Még néhány minta-formátum:
Ha már ott vagyunk, akkor írjuk meg a rekurzív függvényeket tail rekurzívra :)
Még néhány minta-formátum:
- A $\_$ (underscore) minta mindenre illeszkedik (ugyanúgy, mint egy azonosító), de nem bindeli az értéket változóhoz. Ezt praktice akkor használjuk, ha a jobb oldali kifejezésben az itt szereplő értéket nem használnánk semmire.
- Ha az eddigi minták után kettősponttal teszünk egy típust, tehát $P:T$ formátumú lesz a mintánk, ahol $T$ egy típus, akkor ez akkor illeszkedik, ha $P$ illeszkedik és az érték (futásidejű) típusa $T$.
- Ha a mintánk után (lehet benne típusmegjelölés is) egy $\mathrm{if~}B$ alakú ún. if guardot teszünk, ahol $B$ egy $\mathrm{Boolean}$ típusú kifejezés, akkor ez a minta akkor illeszkedik, ha maga a minta illeszkedik és $B$ értéke igaz.
def length(list: Lista): Int = list match { //visszaadja a list elemszámát
case UresLista() => 0
case NemuresLista(_, tail) => 1 + length(tail)
}
def countOdd(list: Lista) : Int = list match { //visszaadja a páratlan elemek számát
case UresLista() => 0
case NemuresLista(h, t) if ( h%2 == 0 ) => countOdd(t)
case NemuresLista(_, t) => 1 + countOdd(t)
}
Elemzés közben ne felejtsük el, hogy az első illeszkedő case jobb oldala kerül kiértékelésre.Ha már ott vagyunk, akkor írjuk meg a rekurzív függvényeket tail rekurzívra :)
No comments:
Post a Comment