Muutettu viimeksi 14.2.2012 / Sivu luotu 22.4.2010 / [oppikirjan esimerkit] / [Scala]
Tässä luvussa vain lyhyesti kurkistetaan tapaan, jolla Scalan kokoelmaluokat on organisoitu. Tarkoitus on tällä esimerkillä vain havainnollistaa Scalan tyypillistä käyttöä ohjelma-arkkitehtuurissa. Parin keskeisen kokoelman työkalujakin silti luetellaan.
Kuten "kuvasta näkyy", myös nuo kaikki kolme tyyppiä on toteutettu piirreluokkina.
Iterable tarjoaa hyvin rikkaan rajapinnan, jonka saa toteuttamalla itse vain erittäin laihan rajapinnan (vrt. luku 12). Piirreluokassa on vain yksi abstrakti metodi,
abstract def iterator: Iterator[A]
Kun siis kokoelmaluokalle toteuttaa iteraattorin,
menetelmän käydä läpi kokoelman alkiot, saa valmiiksi
ohjelmoituna kymmeniä metodeita
(ks. API).
Esimerkiksi seuraavat löytyvät juuri täältä:
exists,
filter,
forall,
foreach,
indexOf,
map,
reduceLeft,
toList, ...
Eivätkä kaikki "ilmaiseksi" saadut metodit ole lainkaan mitään
triviaaleja. Ja kuten jo noista esimerkkimetodeista näkyy,
arkkitehtuuri perustuu hyvin syvästi funktioiden välittämiseen
parametreina.
Alkaako jo valjeta eräiden Scalan ideoiden nerokkuus?
No mikäs sitten oli tuo ainoa toteutetavaksi pyydetty Iterator? Myös se on piirreluokka, mutta Scala-kirjaston luokkahierarkiassa aivan eri paikassa kuin Iterable:
Iterablen tavoin Iterator sisältää suuren joukon toteutettuja metodeita - osin saman nimisiäkin - jotka saa käyttöönsä toteuttamalla kaksi abstraktia metodia:
abstract def hasNext: Boolean
abstract def next: A
Iteraattori ("kulkuri") on kuin sormi, joka osoittaa aluksi
kokoelman "ensimmäiseen" alkioon ja osaa liikkua vain eteenpäin
kokoelman alkioiden joukossa - mitä "eteenpäin" sitten tarkoittaakin.
Sen tietenkin määrittelee ja ohjelmoi se, joka nuo metodit
toteuttaa.
Tavallisesti kokoelmalla on "viimeinen" alkio, mutta tämä ei ole välttämätöntä iteroinnille; Iterator-tyyppinen iteraattori voisi iteroida vaikkapa pitkin piin likiarvon numeroita.
Oppikirjan kaunis luonnehdinta Iterablen Iteratorin
erosta (en osannut itse parempia sanoja tähän keksiä):
The difference between
Iterable and Iterator is that trait Iterable represents types that can
be iterated over (i.e., collection types), whereas trait Iterator is the mechanism
used to perform an iteration. Although an Iterable can be iterated
over multiple times, an Iterator can be used just once. Once you've iterated
through a collection with an Iterator, you can't reuse it. If you need
to iterate through the same collection again, you'll need to call elements on
that collection to obtain a new Iterator.
Tehdään sormiharjoituksena yksi äärettömästi iteroitava luokka:
class ElainArpajaiset extends Iterator[String] {
def hasNext = true
def next = if (math.random < 0.5) "kissa" else "koira"
}
val x = new ElainArpajaiset
for (i <- 1 to 10) print(x.next +", ")
println
// Esimerkkitulostus:
// kissa, kissa, kissa, koira, koira, koira, koira, koira, kissa, koira,
Ja toisena harjoituksena äärellinen iteraatio ja sen käyttö:
class ElainArpajaiset extends Iterator[String] {
var i = 10
def hasNext = i > 0
def next = {i -= 1; if (math.random < 0.5) "kissa" else "koira"}
}
val x = new ElainArpajaiset
while (x.hasNext) print(x.next +", ")
println
// Esimerkkitulostus:
// koira, kissa, koira, kissa, koira, kissa, koira, koira, kissa, kissa,
Kokeillaan vielä piirreluokan Iterable käyttöä:
class ElainArpajaiset extends Iterator[String] {
var i = 10
def hasNext = i > 0
def next = {i -= 1; if (math.random < 0.5) "kissa" else "koira"}
}
class K extends Iterable[String] {
def iterator = new ElainArpajaiset
}
val x = new K
x.foreach( x => print(x + ", ") )
println
// Esimerkkitulostus:
// koira, kissa, koira, koira, koira, kissa, koira, koira, koira, kissa,
Vaikka esimerkit olivat "pelkkää leikkiä" - ja vaikka Scalalla
on kiva leikkiä(!) - selvästi näkee, miten vaivattomasti
ja luontevasti Scalalla saa ohjelmoitua voimakkaita rakenteita!
Kokeillaanpa vielä, miten reilu tuo math.random-arvonta on:
class ElainArpajaiset extends Iterator[String] {
var i = 10000000
def hasNext = i > 0
def next = {i -= 1; if (math.random < 0.5) "kissa" else "koira"}
}
class K extends Iterable[String] {
def iterator = new ElainArpajaiset
}
val x = new K
var kissa, koira = 0
x.foreach( x => if (x=="kissa") kissa += 1 else koira += 1 )
println(kissa + " " + koira)
// Muutama esimerkkisuoritus:
// 4999379 5000621
// 4999595 5000405
// 4999368 5000632
// 4998156 5001844
// 4999904 5000096
Ei kai koiria vain lievästi suosita? ;-)