Sissejuhatus Scalasse
Scala tööriistade info leiad siit.
Scala on mitmeparadigmaline programmeerimiskeel, mis kompileerub Java baitkoodiks. Seetõttu asuvad Scala programmid objektide sees.
object Tervitus { def tervitus(nimi: String): Unit = { println("Tere, " + nimi + "!") } val konstantVäli : Int = 10 var muutujaVäli : Double = 20.0 }
Üldiselt on meetodite süntaks järgnev:
def meetod(argument: tüüp): tagastustüüp = avaldis
Avaldis võib omakorda olla blokk, mille väärtus on siis blokki viimasel real olev väärtus. Scala paneb ise semikoolonid rea lõppu ning neid läheb ainult vaja, kui tahta kõik ühel real kirjutada.
Peameetod on signatuuriga:
def main(args: Array[String]): Unit
Näiteülesanded
Koodi käivitamine
Loome nüüd omad programmi ja käivitame selle! Kirjutage ringi pindala arvutamise meetod ning kutsuge see välja programmi peameetodis. Programm väljastagu umbes selline tekst "Ringi raadiusega 5 cm pindala on 78,5398163397 cm2."
object Basics { def ringiPindala(r: Double): Double = ??? def main(args: Array[String]): Unit = ??? }
Tingimusavaldised
Scalat saab ise avastada oma Haskell ja Java teadmiste põhjal: sisuliselt on asjad nagu Haskell, aga välimuselt nagu Java. Haskelli avaldis if x >= 0 then x else -x
saab näiteks Scalas kirjutada järgmiselt:
def abs(x: Double): Double = { if (x >= 0) x else -x }
Harjutuseks defineerige and ja or if-avaldise abil.
def and(x: Boolean, y: Boolean): Boolean = if (???) ??? else ??? def or (x: Boolean, y: Boolean): Boolean = if (???) ??? else ???
Mittepuhtad arvutused
Erinevalt Haskellist saavad Scala meetodid tekitada nn. kõrvalefekte, s.t. meetodid ei ole puhtad funtksioonid. Lõpetage objekti Summaator definitsioon ja testige seda.
object Summaator { var summa = 0 def liida(x: Int): Unit = ??? def hetkeseis: Int = ??? } object TestiSummaator { def main(args: Array[String]): Unit = { Summaator.liida(10) Summaator.liida(100) assert(Summaator.hetkeseis == 110) } }
for
-tsükklid
Tsükkleid vaatame täpsemalt järgmistel kordadel, aga lihtsaid for
-tsükkleid kirjutatakse nii:
for (muutuja <- algus to lõpp) { ... } // lõpp kaasaarvatud for (muutuja <- algus until lõpp) { ... } // lõpp väljaarvatud for (muutuja <- kollektsioon) { ... } // teistes keeltes foreach
Harjutuseks kirjuta Scala programm, mis trükib konsooli n*n korrutustabeli. Programmi väljund n = 7 jaoks oleks näiteks:
1 2 3 4 5 6 7 2 4 6 8 10 12 14 3 6 9 12 15 18 21 4 8 12 16 20 24 28 5 10 15 20 25 30 35 6 12 18 24 30 36 42 7 14 21 28 35 42 49
Mustrisobitus ja rekursioon listidel
Järgnevates praktikumides vaatame täpsemalt Listide definitsiooni Scalas. Praeguseks võtke lihtsalt teatavaks, et liste saab valmistada süntaksiga List(1,2,3)
aga võib kasutada ka konstruktoreid Nil
(tühilist) ja x :: xs
(mittetühi list).
Tuletame meelde järgmine Haskelli definitsioon mustrite sobitamisega. Seda tuleks nüüd Scala keelde tõlkida. Selleks kasutame match-avaldist:
nullid :: [Int] -> Int nullid [] = 0 nullid (0:xs) = 1 + nullid xs nullid (_:xs) = nullid xs // Täida kõik küsimärgid õigete väärtustega def nullid(l: List[Int]): Int = l match { case Nil => ??? case 0 :: xs => ??? case _ :: xs => ??? }
Nüüd defineerige funktsioon, mis kontollib, et sõnes on sulud tasakaalus. Kõigepealt tuleb lisada enda testifailis mõned testid, näiteks assert(isBalanced("xyz(xy)zy"))
ja assert(!isBalanced(")("))
.
def isBalanced(chars: List[Char]): Boolean = ??? // Järgnev abifunktsioon funktsioon laseb meil mugavamalt seda kasutada, // sest erinevalt Haskellist ei ole sõne automaatselt tähtede list. def isBalanced(str: String): Boolean = isBalanced(str.toList)
Kõrgemat järku funktsioonid listidel
Anonüümsete funktsioonide ehk lambdade süntaks Scalas on järgnev
(muutuja: tüüp, muutja2: tüüp2) => keha
Näiteks listis olevate arvude ühe võrra suurendamiseks:
println(List(1, 2, 3).map((i: Int) => i + 1))
Sellistel lihtsatel juhtudel ei pea tegelikult parameetri tüüpi deklareerima:
println(List(1, 2, 3).map(i => i + 1))
Veelgi enam, kuna parameetrit kasutatakse ühes kohas, siis saab parameetri enda samuti anonüümseks jätta:
println(List(1, 2, 3).map(_ + 1))
Nüüd proovige foldLeft
-iga leida listi elementide summat.
def summa(xs:List[Int]): Int = xs.foldLeft(???)(???)
Ülesanded
Ülesanne 1
Kirjutage meetod summaNullist
, mis võtab argumendiks täisarvu x
ja tagastab arvu, mis on arvude 0
kuni x
summa. Näiteks summaNullist(100) == 5050
.
Ülesanne 2
Selles ülesandes harjutame rekursiooni.
Iteratiivne | Rekursiivne |
---|---|
def iterativeSumTo(n: Int): Int = { var i = 0 var sum = 0 while (i <= n) { sum = sum + i i = i + 1 } sum } |
def recursiveSumTo(n: Int): Int = { def helper(i: Int, sum: Int): Int = { if (i > n) sum else helper(i+1, sum+i) } helper(0,0) } |
Proovige nüüd järgmine natuke üldistatum tsükkel ümber kirjutada rekursioonina!
def iterativeSumFunction(lo: Int, hi: Int, step: Int): Int = { var i = lo var sum = 0 while (i < hi) { sum += i i += step } sum } // Täida kõik küsimärgid! Ja kontrolli üle, et arvutab sama väärtus kui ülemine. def recursiveSumFunction(lo: Int, hi: Int, step: Int): Int = { def helper(i: Int, sum: Int): Int = { if (???) ??? else helper(???, ???) } helper(???, ???) }
Ülesanne 3
Implementeerida meetod loenda
, mis võtab argumendiks arvu x
ja listi xs
.
Meetod arvutab välja, mitu korda esineb arv x
listis xs
. Näiteks
loenda(5, List(1,2,3,4,5,6)) == 1
Ülesanne 4
Kasutades meetodit foldRight
implementeerida sõnede konkateneerimine.
def concat(xs:List[String]): String = ???
Näide:
concat(List("xx","yy","q")) == "xxyyq"
Ülesanded*
Ülesanne 5*
Haskellis väärtustatakse funktsiooni avaldis ainult siis, kui seda vaja on. Scalas, seevastu, väärtustatakse vaikimisi funktsiooni argumendid enne funktsiooni sisenemist. Laisa väärtustamise jaoks tuleb meetodi argumendi tüübi ette lisada =>
. Testige järgnevat koodi:
def neli(x : => Unit): Int = 4 println(neli(println("laisk")))
Kirjutage meetod myAssert
, mis võtab kaks argumenti: esimese tüüp olgu Boolean
ja teise tüübiks String
. Kui tõeväärtus on väär, visata erind AssertionError
andes argumendiks meetodi teise parameetri. Meetodi kutsel ei tohi sõne väärtustada, kui tõeväärtus on tõene.
Ülesanne 6*
Scala keele detailid ei ole kahjuks nii hästi läbi mõeldud, kui Haskelli omad. Näiteks tüüp
(Int,Char) => Boolean
võib tähistada nii
- üheargumendilist funktsiooni, mis võtab paari ja tagastab tõeväärtuse, või
- kaheargumendilise funktsiooni, mis võtab täisarvu, tähe ja tagastab tõeväärtuse.
Proovige järgnevat koodi.
def length(xs:List[Char]): Int = xs.foldLeft(0)((p:(Int, Char)) => p._1 + 1)
Kompilaatori veataeade on veider:
Error:(341, 67) type mismatch; found : ((Int, Char)) => Int required: (Int, Char) => Int
IntelliJ veataeade (tooltip) on nüüd natuke selgem:
Missing parameter: Char