Muutettu viimeksi 27.4.2010 / Sivu luotu 22.4.2010 / [oppikirjan esimerkit] / [Scala]
Sivun sisältöä:
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 elements: 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: AIteraattori ("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 elements = 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 elements = 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: // 5002334 4997666 // 5000385 4999615 // 5001516 4998484 // 5001768 4998232 // 4995602 5004398Ei kai kissoja vain lievästi suosita? ;-)
val colors = List("red", "blue", "green") println(colors.head) // red println(colors.tail) // List(blue, green) println(colors(1)) // blue
val fiveInts = new Array[Int](5) println(fiveInts.toString) // Array(0, 0, 0, 0, 0) val fiveToOne = Array(5, 4, 3, 2, 1) fiveInts(3) = fiveToOne(2) println(fiveInts.toString) // Array(0, 0, 0, 3, 0)
import scala.collection.mutable.ListBuffer val buf = new ListBuffer[Int] buf += 1 buf += 2 println(buf) // ListBuffer(1, 2) 3 +: buf println(buf) // ListBuffer(3, 1, 2) val lista = buf.toList println(lista) // List(3, 1, 2)
import scala.collection.mutable.ArrayBuffer val buf = new ArrayBuffer[Int]() buf += 12 buf += 15 println(buf) // ArrayBuffer(12, 15) 97 +: buf println(buf) // ArrayBuffer(97, 12, 15) println(buf.length) // 3 println(buf(0)) // 97
import scala.collection.immutable.Queue val empty = new Queue[Int] println(empty) // Queue() val has1 = empty.enqueue(1) println(has1) // Queue(1) val has123 = has1.enqueue(List(2, 3)) println(has123) // Queue(1, 2, 3) val (element, has23) = has123.dequeue println(element) // 1 println(has23) // Queue(2, 3)
import scala.collection.mutable.Queue val queue = new Queue[String] queue += "a" queue ++= List("b", "c") println(queue) // Queue(a, b, c) println(queue.dequeue) // a println(queue) // Queue(b, c)
import scala.collection.mutable.Stack val stack = new Stack[Int] stack.push(1) println(stack) // Stack(1) stack.push(2) println(stack) // Stack(1, 2 println(stack.top) // 2 println(stack) // Stack(1, 2) println(stack.pop) // 2 println(stack) // Stack(1)
val nums = Set(1, 2, 3) Creates an immutable set (nums.toString returns Set(1, 2, 3)) nums + 5 Adds an element (returns Set(1, 2, 3, 5)) nums - 3 Removes an element (returns Set(1, 2)) nums ++ List(5, 6) Adds multiple elements (returns Set(1, 2, 3, 5, 6)) nums -- List(1, 2) Removes multiple elements (returns Set(3)) nums ** Set(1, 3, 5, 7) Takes the intersection of two sets (returns Set(1, 3)) nums.size Returns the size of the set (returns 3) nums.contains(3) Checks for inclusion (returns true) ------------------------------------------------------------------ import scala.collection.mutable Makes the mutable collections easy to access val words = mutable.Set.empty[String] Creates an empty, mutable set (words.toString returns Set()) words += "the" Adds an element (words.toString returns Set(the)) words -= "the" Removes an element, if it exists (words.toString returns Set()) words ++= List("do", "re", "mi") Adds multiple elements (words.toString returns Set(do, re, mi)) words --= List("do", "re") Removes multiple elements (words.toString returns Set(mi)) words.clear Removes all elements (words.toString returns Set())Kuvauksien operaatioita:
val nums = Map("i" -> 1, "ii" -> 2) Creates an immutable map (nums.toString returns Map(i -> 1, ii -> 2)) nums + ("vi" -> 6) Adds an entry (returns Map(i -> 1, ii -> 2, vi -> 6)) nums - "ii" Removes an entry (returns Map(i -> 1)) nums ++ List("iii" -> 3, "v" -> 5) Adds multiple entries (returns Map(i -> 1, ii -> 2, iii -> 3, v -> 5)) nums -- List("i", "ii") Removes multiple entries (returns Map()) nums.size Returns the size of the map (returns 2) nums.contains("ii") Checks for inclusion (returns true) nums("ii") Retrieves the value at a specified key (returns 2) nums.keys Returns the keys (returns an Iterator over the strings "i" and "ii") nums.keySet Returns the keys as a set (returns Set(i, ii)) nums.values Returns the values (returns an Iterator over the integers 1 and 2) nums.isEmpty Indicates whether the map is empty (returns false) ------------------------------------------------------------------ import scala.collection.mutable Makes the mutable collections easy to access val words = mutable.Map.empty[String, Int] Creates an empty, mutable map words += ("one" -> 1) Adds a map entry from "one" to 1 (words.toString returns Map(one -> 1)) words -= "one" Removes a map entry, if it exists (words.toString returns Map()) words ++= List("one" -> 1, "two" -> 2, "three" -> 3) Adds multiple map entries (words.toString returns Map(one -> 1, two -> 2, three -> 3)) words --= List("one", "two") Removes multiple objects (words.toString returns Map(three -> 3))