Viimeksi päivitetty 22.2.2016 / Sivu luotu 3.3.2010 / [oppikirjan esimerkit] / [Scala]
Ohjelmointikieliä on paljon, hyvin paljon. Scala on yksi uusi monien joukossa. Kielen viralliset sivut ovat osoitteessa http://www.scala-lang.org/ . Tuolta löytyy tietoa laidasta laitaan spesifikaatiosta ja APIsta aloittelijan oppaisiin.
Tämän sivun sisältöä:
Esimerkki: Ohjelma laskee syöttölukujen keskiarvon. Lukumäärä pyydetään.
import java.util.Scanner; public class Karvo { private static Scanner lukija = new Scanner(System.in); public static void main(String[] args) { System.out.println("Monenko luvun keskiarvo lasketaan? "); int lukujenLkm = lukija.nextInt(); double lukujenSumma = 0; int monesko = 0; while (monesko < lukujenLkm) { monesko = monesko+1; System.out.print("Anna "+monesko+". luku: "); double luku = lukija.nextDouble(); lukujenSumma += luku; } if (lukujenLkm <= 0) System.out.println("\nEi lukuja, ei keskiarvoa.\n"); else { double lukujenKarvo = lukujenSumma/lukujenLkm; System.out.println("\nKeskiarvo on "+lukujenKarvo+".\n"); } } }
import scala.io.StdIn._ println("Monenko luvun keskiarvo lasketaan? "); val lukujenLkm = readInt var lukujenSumma = 0.0 var monesko = 0 while (monesko < lukujenLkm) { monesko = monesko+1 print("Anna "+monesko+". luku: ") val luku = readDouble lukujenSumma += luku } if (lukujenLkm <= 0) println("\nEi lukuja, ei keskiarvoa.\n"); else { val lukujenKarvo = lukujenSumma/lukujenLkm; println("\nKeskiarvo on "+lukujenKarvo+".\n"); }Ylläolevat rivit voidaan kirjoittaa tekstitiedostoon ja suorittaa sellaisenaan komennolla:
Java-ohjelmahan joudutaan aina ensin kääntämään tavukoodiksi ja sitten erikseen suorittamaan tulkilla.
scala> 1+2 res0: Int = 3 scala> println("Hoi maailma!") Hoi maailma! scala> var a=5 a: Int = 5 scala> while (a>0) { | println(a) | a=a-1 | } 5 4 3 2 1 scala> def neliosumma(a:Int, b:Int)= | a*a+b*b scala> neliosumma: (a: Int, b: Int)Int scala> neliosumma(3,4) res3: Int = 25 scala> neliosumma(b=2, a=4) // !! res4: Int = 20 scala> sys.exit()
import scala.io.StdIn._ println("Montako tervehdystä?") var lkm = readInt while (lkm>0) { println("Hoi maailma!") lkm = lkm-1 }Suoritus komennolla
Komento ensin kääntää Scala-ohjelman Javan tavukoodiksi ja sitten suorittaa tuon koodin Javan virtuaalikoneella.
import scala.io.StdIn._ object tervehdi { def main(args: Array[String]) = { println("Montako tervehdystä?") var lkm = readInt while (lkm>0) { println("Hoi maailma!") lkm = lkm-1 } } }Ohjelma Javan tapaan käännetään ensin tavukoodiksi
Vieläkin vähemmällä vaivalla voi päästä "miksaamalla" olioon tervehdi "traitti" eli liittämällä siihen piirretyyppi (trait) nimeltään App:
import scala.io.StdIn._ object tervehdi extends App { println("Montako tervehdystä?") var lkm = readInt while (lkm>0) { println("Hoi maailma!") lkm = lkm-1 } }App-piirretyyppi tuo olioon pääohjelman. Piirretyypeistä lisää aikanaan ... ja paljon ...
#!/usr/bin/scala !# import scala.io.StdIn._ println("Montako tervehdystä?") var lkm = readInt while (lkm>0) { println("Hoi maailma!") lkm = lkm-1 }Suoritusoikeus tiedostolle on luonnollisesti annettava. Ohjelman voi nyt käynnistää pelkällä tiedostonimellä, jonka eteen kirjoitetaan "./". (Hakupolkuasetuksilla tuosta etuliitteestäkin voi päästä eroon, mutta ei ns. kuulu kurssiin... ;-)
def sort(xs: Array[Int]) { def swap(i: Int, j: Int) { val t = xs(i); xs(i) = xs(j); xs(j) = t } def sort1(l: Int, r: Int) { val pivot = xs((l + r) / 2) var i = l; var j = r while (i <= j) { while (xs(i) < pivot) i += 1 while (xs(j) > pivot) j -= 1 if (i <= j) { swap(i, j) i += 1 j -= 1 } } if (l < j) sort1(l, j) if (j < r) sort1(i, r) } sort1(0, xs.length - 1) }Tässä huomiota kannattaa kiinnittää mm. siihen, miten Scala Algolin ja Pascalin tapaan sallii sisäkkäiset aliohjelmat. C-pohjaisissa kielissä - Java mukaan lukien (!) - aliohjelmarakenne on "litteä": niissä aliohjelma ei voi sisältää omia "pikku apulaisiaan" Algolin, Pascalin ja monien modernien kielten tapaan! Taulukon indeksoinnissa Scalassa käytetään kaarisulkeita tutumpien hakasulkeiden síjaan.
import scala.language.postfixOps def sort(xs: Array[Int]): Array[Int] = if (xs.length <= 1) xs else { val pivot = xs(xs.length / 2) Array.concat( sort(xs filter (pivot >)), xs filter (pivot ==), sort(xs filter (pivot <)) ) }Tässä huomio kannattaa kiinnittää muutamaan seikkaan: Array.concat liittää yhteen kolme osataulukkoa. Ensimmäiseen suodatetaan kaikki jakoalkiota pienemmät alkiot, keskimmäiseen jakoalkion kanssa yhtä suuret ja kolmanteen suuremmat. Suodattimelle (filter) annetaan argumenttina ns. predikaattifunktio tyyliin "pivot >", mikä todellakin tarkoittaa sitä, että vertailun operaatio annetaan parametrina. Scalassa funktiot ovat arvoja siinä kuin numeeriset arvotkin, funktioita voidaan välittää parametreina, muuttujan arvona voi olla funktio, funktio voi palauttaa arvonaan funktion, käytettävissä ovat funktioliteraalit siinä kuin numeeriset literaalitkin...
var a = new Array[Int](4) a(0) = 4 a(1) = 3 a(2) = 8 a(3) = 1Alkiot voidaan käydä läpi indeksoiden tai "for each" -tyyliin:
for (i <- 0 to 3) println(a(i)) for (e <- a) // "foreach a'la Java" println(e) a.foreach(println) // "oikea" foreachAivan vastavalla tavalla voidaan käsitellä listoja:
val a = List(4, 3, 8, 1) for (i <- 0 to 3) println(a(i)) for (i <- a) // "foreach" a'la Java println(i) a.foreach(println) // "oikea" foreach
// määritellään pari funktiota ja luodaan lista: def muotoile(muotoilija: String => String, sanat: List[String]) = for(sana <- sanat) print(muotoilija(sana)) def huuda(x: String) = x.toUpperCase val lista = List("hip ", "hurraa!") // annetaan parametrina ensin nimetty funktio ja lista: muotoile(huuda, lista) println // tulostus: HIP HURRAA! // annetaan sitten parametrina funktioliteraali ja lista: muotoile({x => x.replace('h', 'j')}, lista) println // tulostus: jip jurraa! // tässä tilanteessa edes aaltosulkeet eivät ole välttämättömiä: muotoile(x => x.replace('h', 'B'), lista) println // tulostus: Bip Burraa!Esimerkissä siis funktion muotoile muodollinen funktioparametri määriteltiin muodossa: muotoilija: String => String ja vastaavina todellisina parametreina käytettiin nimettyä funktiota huuda ja funktioliteraalia {x => x.replace('h', 'j')}.
class PosPariton (private var luku: Int) { // pääkonstruktorin parametreista tulee kenttiä! // --- "pääkonstruktorin algoritmi" --- aseta(luku) // -- apukonstruktori --- def this() {this(1)} // parametrittomana oletusarvo on 1 // --- ottavat aksessori eli getteri: --- def arvo() = luku // --- asettavat aksessorit eli setterit: --- def aseta(a: Int) { luku = a if (luku<0) luku = -luku if (luku%2==0) luku += 1 } def +=(a: Int) = { // += kelpaa mainiosti aksessorin tunnukseksi! aseta(luku+a) } // korvataan peritty toString override def toString = "Positiivinen pariton on nyt " + luku } // Kokeillaan: val x = new PosPariton(8) // huom: x:ää ei voi muuttaa, mutta oliota voi println(x) // Positiivinen pariton on nyt 9 x.aseta(-200) println(x.arvo()) // 201 var y = new PosPariton() println(y) // Positiivinen pariton on nyt 1 val z = new PosPariton(3) z.+=(3) println(z) // Positiivinen pariton on nyt 7 z += 7 // edes piste ja sulut eivät ole aksessoinnissa tarpeen! println(z) // Positiivinen pariton on nyt 15 z += -16 println(z) // Positiivinen pariton on nyt 1Huomioita:
z.+=(3) // nämä tarkoittavat samaa! z += 3
Asiat voivat olla helppoja:
var capital = Map("US" -> "Washington", "France" -> "Paris") capital += ("Japan" -> "Tokyo") println(capital("France")) // Paris var pk = Map("Suomi" -> "Helsinki", "Ruotsi" -> "Tukholma") println(pk("Ruotsi")) // Tukholma println(pk) // Map(Suomi -> Helsinki, Ruotsi -> Tukholma) println(pk + ("Viro" -> "Tallinna")) // Map(Suomi -> Helsinki, Ruotsi -> Tukholma, Viro -> Tallinna) println(pk - ("Ruotsi")) // Map(Suomi -> Helsinki)Helppoa ja hauskaa. Näyttää vaarattomalta, mutta sisältää itse asiassa mielenkiintoisia juttuja:
Omat tyypit ja operaatiot:
Scalassa BigInt on kirjastossa. Vastaa Javan BigInteger-luokkaa (itse asiassa "wräppää" tuon luokan). Kyseessä siis on kooltaan rajoittamaton kokonaislukutyyppi. Mutta ero löytyy helppokäyttöisyydessä.
Scalalla voi ohjelmoida seuraavasti:
def factorial(x: BigInt): BigInt = if (x == 0) 1 else x * factorial(x - 1) println(factorial(30)) // 265252859812191058636308480000000Toki Javan luokkaakin voi käyttää, mutta, mutta:
import java.math.BigInteger def factorial(x: BigInteger): BigInteger = if (x == BigInteger.ZERO) BigInteger.ONE else x.multiply(factorial(x.subtract(BigInteger.ONE))) println(factorial(new BigInteger("30"))) // 265252859812191058636308480000000
Luokka BigInt nyt vaan sattuu olemaan Scalan standardikirjastossa. Ja sille on ohjelmoitu mm. metodit +, -, *, ... Ja toki metodikutsun saa kirjoittaa esim. Javasta tuttuun tapaan:
def factorial(x: BigInt): BigInt = if (x == 0) 1 else x.*(factorial(x - 1))Myös molemmat:
def factorial(x: BigInt): BigInt = if (x == 0) 1 else x.*(factorial(x.-(1)))Ja toki yhtäsuuruusvertailun voi tehdä toisinkin
def factorial(x: BigInt): BigInt = if (x.equals(0)) 1 else x.*(factorial(x.-(1)))Vielä kerran: BigInt siis ei ole kieleen sisäänrakennettu tyyppi vaan kirjastoon ohjelmoitu tyyppi. Vastaavia voi siis tehdä myös itse!
Myös kontrollirakenteita voi ohjelmoida itse, ja esimerkiksi for-lause on itse asiassa kirjastofunktio!