Tämä kurssi on jo päättynyt.

Luku 3.3: Kokemuksia ja totuusarvoja

Tästä sivusta:

Pääkysymyksiä: Millaisia luokkia on GoodStuff-esimerkkiohjelmassa? Miten vertailen arvoja keskenään? Miten ilmaisen ohjelmassa, onko tietty arvo suurempi kuin toinen, tai vastaavia "juu tai ei" -tietoja? Miten saan ötökän mätkähtämään päin estettä?

Mitä käsitellään? Totuusarvot, Boolean-tietotyyppi ja vertailuoperaattorit.

Mitä tehdään? Luetaan ja kokeillaan itse mukana.

Suuntaa antava työläysarvio:? Pari tuntia.

Pistearvo: A70.

Oheismoduulit: GoodStuff, Oliointro, Odds, FlappyBug. Vapaaehtoisessa tehtävässä esiintyy myös Miscellaneous (uusi).

../_images/person05.png

Kertausta: GoodStuff

Luvussa 2.1 tutustuit esimerkkiin siitä, miten GoodStuff-ohjelman luokat toimivat yhteen. Se kannattaa nyt kerrata. Tässä uusinta:

GoodStuff-ohjelman osat

Tässä luvussa alamme tutkia GoodStuff-sovelluksen Scala-ohjelmakoodia. Se koostuu seuraavista osista.

../_images/goodstuff-fi.png

GoodStuff-ohjelman käyttöliittymä. Iloisella irvinaamalla merkitään se kokemus, jolle käyttäjä on antanut korkeimman arvosanan.

Pakkaus o1.goodstuff:

  • Experience-luokka kuvaa kokemuksista tehtyjä muistikirjamerkintöjä; yksi luokan ilmentymä on yksi merkintä. Tätä luokkaa tutkitaan tässä luvussa ja vähän seuraavassakin.

  • Category-luokan avulla voidaan koota useita kokemusolioita yhteen kokemuskategoriaan. Kullakin kategorialla on nimi ja hinnoitteluyksikkö (esim. hotelliyö). Kategoriaan myös liittyy tieto siitä, mikä kyseisen kategorian kokemuksista on käyttäjän suosikki. Tähän luokkaan tutustumme luvussa 4.2.

Pakkaus o1.goodstuff.gui:

  • CategoryDisplayWindow on luokka, joka kuvaa eräänlaisia käyttöliittymäikkunoita. Annetussa muodossaan GoodStuff-sovelluksessa on tasan yksi tällainen ikkuna hotellikategorialle, mutta periaatteessa niitä voisi olla useitakin. Ikkuna on toteutettu Swing-kirjastolla, josta kerrotaan luvussa 12.4.

  • GoodStuff on yksittäisolio, jolla on erikoistehtävä: se toimii sovelluksen käynnistysoliona (luku 2.7). Kyseessähän on tiedosto GoodStuff.scala, josta käsin olet käynnistänyt sovelluksen IntelliJ’ssä.

Tässä luvussa siis keskitymme luokkaan Experience. Samalla osoittautuu oitis, että on syytä tutustua erääseen uuteen Scalan perustietotyyppiin ja siihen liittyviin yleisempiin ohjelmoinnin käsitteisiin.

Lue dokumentaatiota!

Etsi nyt käsiisi Experience-luokan Scaladoc-dokumentaatio GoodStuff-moduulista IntelliJ’ssä tai tämän sivun alun moduulilinkistä. Lue se. Jatka vasta, kun olet lukenut sen.

Muiden samaan moduuliin kuuluvien luokkien dokumentaatiota ei ole tarpeen nyt lukea.

Experience-kokeilu

Kokeillaan Experience-luokan käyttöä REPLissä. Experience-oliolle annetaan luotaessa konstruktoriparametreiksi nimi, kuvaus, hinta ja arvosana. Annetussa sovelluksessa merkinnät koskivat hotellikokemuksia, mutta se ei ole oleellista. Tehdään nyt vaihtelun vuoksi vaikkapa viinikokemuksia kuvaavia olioita:

val wine1 = Experience("Il Barco 2001", "ookoo", 6.69, 5)wine1: Experience = o1.goodstuff.Experience@a62427

Experience-luokkaa voi käyttää sen Scaladoc-dokumentaatiossa kuvatulla tavalla. Tässä pari esimerkkiä:

wine1.nameres0: String = Il Barco 2001
wine1.valueForMoneyres1: Double = 0.7473841554559043

Jotta GoodStuff-ohjelma voisi pitää kirjaa tietyn kategorian suosikista, sen täytyy pystyä vertailemaan kokemusmerkintöjä niiden arvosanojen perusteella. Experience-luokkaan on määritelty kaksikin tähän liittyvää metodia: isBetterThan ja chooseBetter. Näistä ensimmäisen ymmärtäminen edesauttaa jälkimmäisen ymmärtämistä; siksi aloitamme siitä. Kokeillaan nyt isBetterThan-metodia ja pureudutaan chooseBetter-metodiin seuraavassa luvussa.

val wine2 = Experience("Tollo Rosso", "turhahko", 6.19, 3)wine2: Experience = o1.goodstuff.Experience@140b3c7
wine2.isBetterThan(wine1)res2: Boolean = false

Kun kokemusolion isBetterThan-metodia kutsutaan, sille pitää antaa parametriksi viittaus johonkin kokemusolioon, johon "puhuteltavaa" oliota verrataan.

Metodi vertailee toisiinsa sitä kokemusta, jolle metodia on kutsuttu, ja sitä, joka on annettu parametriksi. Se palauttaa arvon, joka kertoo, onko ensin mainitun olion arvosana parametriolion arvosanaa korkeampi.

Palautusarvo ei kuitenkaan näytä ihan tutulta. Mikä on arvo false ja mikä on Boolean-tietotyyppi, jota se edustaa?

Totuusarvot ja Boolean

Kaksijakoiset tilanteet ovat ohjelmissa erittäin yleisiä: Onko tämä luku suurempi kuin tuo? Löytyvätkö tietyn henkilön asiakastiedot ohjelmaan kirjatuista tiedoista? Onko käyttäjä laittanut rastin tiettyyn ruutuun? Valitsiko pelaaja mustat vai valkoiset nappulat? Oliko Shift-näppäin pohjassa, kun hiirellä klikattiin? Onko ötökkä törmännyt esteeseen?

On siis tarve totuusarvoille, tarkemmin sanoen Boolen logiikalle (Boolean logic). Boolen logiikassa käytetään tasan kahta totuusarvoa, tosi ja epätosi.

Boolean-tyypistä

"Totta" ja "tarua" vastaavat Scalan literaalimerkinnät true ja false (vrt. luku- ja merkkijonoliteraalit). Nämä Boolean-tyyppiset literaalit ovat Scalan varattuja sanoja, eli niitä ei voi käyttää esimerkiksi muuttujien niminä.

Scalassa totuusarvoja kuvaa tietotyyppi Boolean, joka on määritelty pakkauksessa scala ja on siis aina käytettävissä Scala-ohjelmissa Int- ja String-tyyppien tapaan.

val paaviOnKatolilainen = truepaaviOnKatolilainen: Boolean = true
val maapalloOnLittea = falsemaapalloOnLittea: Boolean = false

Mitään muita Boolean-arvoja truen ja falsen lisäksi ei ole olemassa. (Vertaa: erilaisia merkkijonoja ja lukuja on vaikka kuinka paljon.)

Pelkkä yksittäinen totuusarvoliteraalikin on lauseke, jonka arvo on kyseinen totuusarvo itse:

falseres3: Boolean = false

Tarvitaanko erillinen Boolean?

Voisiko isBetterThan-metodi palauttaa merkkijonon "yes" tai "no" Boolean-arvon sijaan? Tai jonkin luvun: sovitaan vaikkapa, että nolla tarkoittaa epätotta ja kaikki muut luvut totta?

Periaatteessa kyllä, ja käytännössäkin jälkimmäistä ehdotusta sovelletaan joissakin ohjelmointikielissä. Kuitenkin Scalassa ja monessa muussa ohjelmointikielessä on erillinen tietotyyppi totuusarvojen kuvaamiseen. Erillisen tietotyypin käyttäminen usein selkeyttää ohjelmakoodia ja mahdollistaa myös paremmat automaattiset virhetarkastukset ja -ilmoitukset. Näin voidaan esimerkiksi varmistaa, että totuusarvomuuttujaan ei vahingossakaan sijoiteta muuta kuin juuri totuusarvo.

Totuusarvojen käyttöä

Voit käyttää totuusarvoja tuttuun tapaan esimerkiksi muuttujiin sijoituksissa, parametrilausekkeissa ja metodien palautusarvoina:

println("Totuus on " + paaviOnKatolilainen)Totuus on true
val oliParempi = wine2.isBetterThan(wine1)oliParempi: Boolean = false
val viiniOnParempiKuinSeItse = wine2.isBetterThan(wine2)viiniOnParempiKuinSeItse: Boolean = false

Lukuihin ja merkkijonoihin liittyy operaattoreita kuten +. Niin totuusarvoihinkin. Yksi totuusarvo-operaattoreista on !. Tämä huutomerkkioperaattori luetaan "ei" tai englantilaisittain "not". Se "kääntää" totuusarvon, joka sen perään kirjoitetaan, eli falsesta tulee true ja toisin päin. Esimerkiksi:

!oliParempires4: Boolean = true

Tässä siis lausekkeen !oliParempi arvo on true. Muuttujan oliParempi arvoon tämän lausekkeen evaluoiminen ei vaikuta yhtään mitenkään, vaan muuttujassa on edelleen false.

Sen lisäksi, että voidaan käyttää literaaleja, totuusarvoja syntyy myös arvoja vertailemalla:

Vertailuoperaattorit

Scalan vertailuoperaattoreilla voi muodostaa lausekkeita, joissa vertaillaan osalausekkeiden arvoja.

Operaattori

Merkitys

>

suurempi kuin

<

pienempi kuin

>=

suurempi tai yhtä suuri kuin

<=

pienempi tai yhtä suuri kuin

==

yhtä suuri kuin

!=

erisuuri kuin

Huomaa, että vertailuoperaattorit kirjoitetaan <= ja >=. Niitä ei kirjoiteta =< tai => (joista jälkimmäisellä on Scalassa ihan muu merkitys; siitä lisää myöhemmin). Muistisääntö: kirjoita samassa järjestyksessä kuin luetkin: pienempi (<) tai yhtä suuri (=) kuin.

Lukujen vertailua

Vertaileminen tuottaa Boolean-arvoja:

10 <= 10res5: Boolean = true
20 < (10 + 10)res6: Boolean = false
val ikavuodet = 20ikavuodet: Int = 20
val onAikuinen = ikavuodet >= 18onAikuinen: Boolean = true
ikavuodet == 30res7: Boolean = false
20 != ikavuodetres8: Boolean = false

Vertailtaviksi voi kirjoittaa mitä vain tietotyypiltään sopivia lausekkeita. Ne evaluoidaan ja niiden arvoja verrataan toisiinsa.

Yhtäsuuruutta vertaillessa on aina käytettävä kahta yhtäsuuruusmerkkiä. Tämä unohtuu aluksi helposti. Yksi yhtäsuuruusmerkkihän on käytössä ihan muissa yhteyksissä, kuten muuttujaan sijoittamisessa ja metodin määrittelyn alussa.

Alla on totuusarvoja sisältävä animaatio. Osaatko jo animaatiota katsomatta päätellä pelkästä koodista, mitä se tulostaa?

Erilaisia vertailuja

Kokeile tässä välissä REPLissä totuusarvoja ja vertailuoperaattoreita.

Kokeile operaattoreita myös muilla arvoilla kuin luvuilla. Merkkijonoja voi vertailla samaan tapaan kuin lukuja, ja Boolean-arvoja voi myös vertailla keskenään.

Jatka eteenpäin seuraaviin harjoituksiin, kun olet kokeillut vertailuoperaattoreiden käyttöä REPLissä.

Vertailuharjoituksia

Tarvitsemme totuusarvoja ja vertailuoperaattoreita jatkossa vähän väliä. Olisi hyvä, jos ne tulisivat mahdollisimman pian sujuvasti selkärangasta, jotta et joudu niitä liikaa miettimään monimutkaisempaa kokonaisuutta laatiessa tai lukiessa. Niinpä alla on koko joukko pieniä totuusarvoharjoitteita. Kokeile/päättele ja kirjoita vastaukset. Älä tässäkään tyydy vain arvaamaan tai kopioimaan REPListä, vaan mieti myös miksi ja koettele päätelmiäsi REPLissä.

Oletetaan annetuiksi nämä muuttujien alustukset:

val iso = 10000
val pieni = 10

Valitse kussakin seuraavista kohdista mainitun lausekkeen arvo.

iso > pieni

pieni < iso

!(iso > pieni)

"P" == "NP"

"kissa" + "kala" != "kissakala"

"mun isä" > "sun isä"

"aaaaaaaaaa" < "bb"

false == true

true == true

false == false

(10 < 20) != (20 > 10)

"laama" < 100

"100" < 100

"100" == 100

Oletetaan annetuiksi nämä käskyt:

var luku = 19
println(luku / 20)
val onNolla = luku == 0

Mikä on nyt muuttujan onNolla arvo?

Annetaan äskeisten lisäksi nämä käskyt:

luku = 0
println(onNolla)

Mitä tulostuu?

Annetaan vielä lisäksi nämä käskyt:

luku = 20
println(luku + luku * 10 < (luku + luku) * 5)

Mitä tulostuu?

Annetaan sitten tämä käsky:

println((19 >= luku) == (0 >= luku))

Mitkä seuraavista pitävät paikkansa?

Ja vielä lopuksi tämäkin:

println(0 >= luku != onNolla)

Mitä tämä käsky tulostaa?

Olkoon määriteltyinä kaksi kokonaislukumuuttujaa luku1 ja luku2 sekä kaksi kokemusolioon viittaavaa muuttujaa exp1 ja exp2. Näiden muuttujien täsmällisillä arvoilla ei ole tehtävän kannalta merkitystä. Mitkä kaikki seuraavista pitävät joka tapauksessa paikkansa?

Minitehtävä: pystykuva

Kuvia jaotellaan joskus pysty- ja vaakakuviin (portrait vs. landscape). Pystykuvan korkeus on suurempi kuin sen leveys ja vaakakuvalla vastaavasti toisin päin.

Nouda Miscellaneous-moduuli. Älä välitä nyt sen muusta sisällöstä; paikanna vain tiedosto misc.scala. Kirjoita tuohon tiedostoon vaikutukseton funktio isPortrait, joka

  • ottaa yhden Pic-tyyppisen parametrin

  • palauttaa true, jos annetun kuvan korkeus on leveyttä suurempi; ja

  • palauttaa false muutoin (eli jos kuva on neliön muotoinen tai vaakakuva).

A+ esittää tässä kohdassa tehtävän palautuslomakkeen.

Olioiden yhtäsuuruudesta

Olioita voi vertailla toisiinsa operaattoreilla == ja !=. Mutta mitä yhtäsuuruus tarkoittaa vaikkapa kokemusolioiden tapauksessa?

Tutki asiaa itse REPLissä vastatessasi seuraavaan tehtävään. (Käynnistä REPL GoodStuff-moduuliin.)

Oletetaan annetuiksi nämä käskyt:

val wine1 = Experience("Il Barco 2001", "ookoo", 6.69, 5)
val wine2 = Experience("Tollo Rosso", "turhahko", 6.19, 3)
val wine3 = wine1
val wine4 = Experience("Il Barco 2001", "ookoo", 6.69, 5)

Mitkä seuraavista lausekkeista ovat nyt arvoltaan true?

Huomaat: pelkkä kokemusolioiden ominaisuuksien keskinäinen samankaltaisuus ei riittänyt tekemään niistä "yhtä suuria".

Esimerkit ==-operaattorista yllä vertailivat pelkästään muuttujien sisältöä eli niitä viittauksia, jotka muuttujiin on tallennettu. Ne eivät verranneet varsinaisia olioita lainkaan. Esimerkiksi wine1 == wine4 on false, koska kyseessä ei ole kaksi viittausta samaan olioon vaan kaksi viittausta kahteen samanlaiseen mutta erilliseen olioon. wine1 == wine3 taas on true, koska molempien muuttujien arvona on viittaus täsmälleen samaan olioon; viittaukset vievät samaan paikkaan.

Yleisesti ottaen se, miten yhtäsuuruusvertailu olioille toimii, riippuu olioiden tyypistä. Itse määritetyille tietotyypeille se on oletusarvoisesti ns. identiteettivertailu eli toimii kuin kokemusolioille yllä. Toisaalta monilla Scalan valmiilla tietotyypeillä on tietotyyppikohtaiset määritelmät yhtäsuuruudelle. Hyvä esimerkki tästä ovat puskurit: on määritelty, että kaksi kokonaan erillistäkin puskuria ovat yhtäsuuret, jos niillä on sama sisältö. Tämä käy ilmi seuraavasta REPL-esimerkistä:

val erasPuskuri = Buffer(2, 10, 5, 4)erasPuskuri: Buffer[Int] = ArrayBuffer(2, 10, 5, 4)
val toinenPuskuri = Buffer(2, 10, 5)toinenPuskuri: Buffer[Int] = ArrayBuffer(2, 10, 5)
erasPuskuri == toinenPuskurires9: Boolean = false
toinenPuskuri += 4res10: Buffer[Int] = ArrayBuffer(2, 10, 5, 4)
erasPuskuri == toinenPuskurires11: Boolean = true

Experience-luokan toteutus

Vertailuoperaattorein varustettuina voimme nyt laatia Experience-luokalle ohjelmakoodin. Aloitetaan hahmottelemalla toteutus pseudokoodina:

class Experience(vaadi konstruktoriparametreiksi nimi, kuvaus, hinta ja arvosana, jotka
                 kaikki tallennetaan sellaisenaan kiintoarvoisiin ilmentymämuuttujiin):

  def valueForMoney = palauta arvosanasi ja hintasi osamäärä

  def isBetterThan(another: Experience) = palauta totuusarvo, joka kertoo,
                                          onko oma arvosanasi korkeampi kuin
                                          parametriksi annetun kokemusolion arvosana

end Experience

Vertailumetodimme saa parametriksi viittauksen vertailukohtana olevaan Experience-olioon. Parametrimuuttujan nimi voi olla esimerkiksi another. Ajatus on tältä osin samankaltainen kuin Pos-luokan xDiff- ja yDiff-metodeissa (luku 2.5), joissa myös käsiteltiin this-oliota ja parametrina osoittamaa toista saman luokan ilmentymää.

Tässä Scala-toteutus:

class Experience(val name: String, val description: String, val price: Double, val rating: Int):

  def valueForMoney = this.rating / this.price

  def isBetterThan(another: Experience) = this.rating > another.rating

end Experience

Mitään muuta uutta tässä esimerkissä ei ole, kuin että metodin palautusarvo tuotetaan vertailuoperaattorilla ja on siis Boolean-tyyppinen.

Pikkutehtävä: totuusarvoinen ilmentymämuuttuja

Valmisteleva kertaus

Palautetaan mieleen Asiakas- ja Tilaus-luokat:

val testihenkilo = Asiakas("T. Testaaja", 12345, "test@test.fi", "Testitie 1, 00100 Testaamo")testihenkilo: Asiakas = o1.luokkia.Asiakas@a7de1d
val testitilaus = Tilaus(10001, testihenkilo)testitilaus: Tilaus = o1.luokkia.Tilaus@18c6974

Tehtävänanto

Boolean-arvot sopivat myös olion ominaisuuksien kuvaamiseen. Esimerkiksi kustakin tilauksesta voitaisiin kirjata, onko se kallis mutta nopea pikatilaus vai ei. Tilauksilla voisi tällöin olla onPika-niminen ilmentymämuuttuja:

testitilaus.onPikares12: Boolean = false
testitilaus.onPika = true

Lisää Oliointro-moduulin Tilaus-luokkaan tällainen ilmentymämuuttuja. Sen arvon tulee siis olla aluksi false, mutta arvoa voi muuttaa sijoituskäskyllä kuten yllä.

A+ esittää tässä kohdassa tehtävän palautuslomakkeen.

Odds-tehtävä (osa 6/9)

Hyödynnetään Boolean-tietotyyppiä kehittäessämme viimeksi luvussa 2.7 käsiteltyä Odds-ohjelmaa taas vähän eteenpäin.

(Jos et tehnyt tuohon moduuliin liittyviä tehtäviä viime kierroksella, käy aiemmat vaiheet nyt läpi; voit käyttää tälle tehtävälle pohjana niiden esimerkkiratkaisuja, jotka löytyvät linkeistä tehtävien kohdalta määräajan jälkeen.)

Tehtävänanto

Laadi metodi isLikely, jolla voi selvittää, onko Odds-olion kuvaama tapahtuma todennäköinen vai ei. Tapahtuma katsotaan tässä todennäköiseksi, jos Odds-olio kuvaa sen toteutumisen olevan todennäköisempää kuin sen toteutumatta jäämisen. Esimerkiksi kuutosen heittäminen nopalla ei ole todennäköistä, mutta "minkä vaan muun kuin kuutosen" heittäminen on:

val rollingSix = Odds(5, 1)rollingSix: Odds = o1.odds.Odds@d4c0c4
val notRollingSix = Odds(1, 5)notRollingSix: Odds = o1.odds.Odds@a5e42e
rollingSix.isLikelyres13: Boolean = false
notRollingSix.isLikelyres14: Boolean = true

Laadi myös metodi isLikelierThan, jolla voi selvittää kahdesta tapahtumasta todennäköisemmän:

rollingSix.isLikelierThan(notRollingSix)res15: Boolean = false
notRollingSix.isLikelierThan(rollingSix)res16: Boolean = true
rollingSix.isLikelierThan(rollingSix)res17: Boolean = false

Ohjeita ja vinkkejä

  • Lisää metodit tiedostoon Odds.scala.

  • Voit käyttää käynnistysoliota OddsTest2 metodiesi testaamiseen. Tuo pikkuohjelma on annettu valmiina, joskin sen sisältö on "kommentoitu ulos", jotta koodi ei tuottaisi virheilmoituksia isLikely- ja isLikelierThan-metodien puuttumisesta ennen kuin olet ne toteuttanut. Poista kommenttimerkit testiohjelmasta, kun olet ensin kirjoittanut pyydetyt metodit Odds-luokkaan, ja aja ohjelma.

  • Kuten luvun 2.7 edellisessä Odds-tehtävässä, myös nyt on tarkoitus, että testiohjelma OddsTest2 kutsuu Odds-luokkaan kirjattuja metodeita. Älä nytkään kopioi Odds-luokan määrittelyä tai sen osia testiohjelmaan.

A+ esittää tässä kohdassa tehtävän palautuslomakkeen.

FlappyBug-tehtävä (osa 11/17: Game Over)

Olet varmasti jo huomannut, ettei ötökkäpelimme este estä mitään. Laitetaan peli pysähtymään esteen koskettaessa ötökkää.

Pohjustus: näkymien isDone-metodi

Luvussa 3.1 tutkimme esimerkkiohjelmaa, joka kasvatti klikatessa ruudulla näkyvää ympyrää:

object klikkausnakyma extends View(klikkauslaskuri):

  def makePic = sininenTausta.place(circle(klikkauslaskuri.arvo, White), Pos(100, 100))

  override def onClick(klikkauskohta: Pos) =
    klikkauslaskuri.etene()
    println("Klikkaus koordinaateissa " + klikkauskohta + "; " + klikkauslaskuri)

end klikkausnakyma

View-oliolle voi lisätä myös isDone-metodin, joka säätelee sitä, milloin käyttöliittymä lakkaa käsittelemästä klikkauksia, tikityksiä ja muita käyttöliittymätapahtumia:

object pysahtyvaKlikkausnakyma extends View(klikkauslaskuri):

  def makePic = sininenTausta.place(circle(klikkauslaskuri.arvo, White), Pos(100, 100))

  override def onClick(klikkauskohta: Pos) =
    klikkauslaskuri.etene()
    println("Klikkaus koordinaateissa " + klikkauskohta + "; " + klikkauslaskuri)

  override def isDone = klikkauslaskuri.arvo > 10

end pysahtyvaKlikkausnakyma

isDone palauttaa Boolean-arvon, joka määrittää, onko käyttöliittymä jo "valmis" eli voidaanko tapahtumankäsittely jättää sikseen. Tässä esimerkissä klikkausten käsittely lopetetaan, kun laskurin arvo on yli 10.

Toteutuksemme korvaa oletustoteutuksen, joka palauttaa aina false eikä siis pysäytä käyttöliittymää lainkaan. Siksi tässäkin override (luku 3.1).

View-olio kutsuu itse omaa isDone-metodiaan kunkin kellonlyömän tai muun tapahtuman jälkeen tarkastaakseen, pitäisikö jo lopettaa. Tähän tarkastustiheyteen voi vaikuttaa (vain) epäsuorasti tikitystahtia säätämällä (luku 3.1).

Tehtävänanto

Tee kolme asiaa:

  1. Määrittele Obstacle-luokkaan metodi, jolla selvitetään, koskettaako este ötökkää.

  2. Määrittele Game-luokkaan äskeistä metodia käyttävä metodi, joka kertoo, onko peli päättynyt pelaajan tappioon esteeseen osumisen vuoksi.

  3. Määrittele FlappyBugin käyttöliittymään isDone-metodi, joka huolehtii siitä, että käyttöliittymä pysähtyy pelin päättyessä.

Tarkemmin sanoen:

  1. Lisää Obstacle-luokkaan vaikutukseton metodi touches, joka selvittää onko este parhaillaan ötökän kohdalla vai ei.

    • Se ottaa ainoaksi parametrikseen viittauksen Bug-olioon.

    • Se palauttaa Boolean-arvon.

    • Se selvittää etäisyyden kyseisen esteen (this) ja parametrin osoittaman ötökän välillä ja muodostaa palautusarvonsa tämän etäisyyden perusteella. Palautusarvo on true jos ja vain jos etäisyys on pienempi kuin ötökän ja esteen säteet (radius) yhteensä; muuten se on false.

    • Etäisyys lasketaan suoraa viivaa pitkin ötökän ja esteen keskipisteiden välillä. Apuna voit käyttää Pos-luokan distance-metodia (luku 2.5).

  2. Lisää Game-luokkaan alla annettu metodi isLost, joka määrittää, onko pelaaja hävinnyt pelin. Tämä metodi käyttää ensin laatimaasi touches-metodia: peli on hävitty nimenomaan silloin, jos pelin (toistaiseksi ainoa) este koskettaa ötökkää.

    def isLost = this.obstacle.touches(this.bug)
    
  3. Lisää FlappyBugin View-oliolle isDone-metodi, joka palauttaa true pelaajan hävittyä pelin ja false muuten. Käytännössä siis peli pysähtyy ötökän osuessa esteeseen.

    • Ota isDone-metodissa mallia yllä olevasta klikkausohjelmasta. Rungoksi riittää yksinkertaisesti yksi isLost-metodikutsu.

A+ esittää tässä kohdassa tehtävän palautuslomakkeen.

Opiskelijakysymys metodien vaikutuksellisuudesta (esim. isLost)

Mietin isLost-metodin osalta: miksi tämä ei ole vaikutuksellinen? Pelihän loppuu, eikö se ole ulkoinen vaikutus? Vai onko se vaikutukseton siksi, koska se vain tarkistaa, osuuko ötökkä esteeseen, ei sinänsä muuta mitään pelin tilaa?

Vaikutuksellisten ja vaikutuksettomien metodien kanssa on vielä vähän hakemista, esimerkiksi [View-luokan] isDone tuntuu kovin vaikutukselliselta, mutta ei ilmeisesti ole.

Tosiaan molemmat mainitut metodit luetaan vaikutuksettomiksi. Se ei tarkoita, etteikö noilla metodeilla voisi olla merkitystä sen kannalta, miten jokin niitä kutsuva ohjelma käyttäytyy. Tuo merkitys vain on epäsuora ja toteutuu noiden metodien ulkopuolella.

Vaikutuksettomuus tarkoittaa, että noiden metodien kutsuminen itsessään ei tuota vaikutusta noiden metodien ulkopuolelta havaittavaan tilaan tai toimintaan. Ne eivät vaihda minkään muuttujan arvoa tai tulosta mitään. Niiden kutsuminen ei itsessään lopeta peliä tai päätä ohjelman suoritusta.

Pelin isLost ainoastaan selvittää erään tiedon pelin tilasta ja raportoi sen kutsujalleen Boolean-arvona. Näkymän isDone vastaavasti vain selvittää erään tiedon näkymän esittämästä mallista (tässä sattumalta juuri isLost-metodia apuna käyttäen).

Se on sitten eri asia, mitä View noita metodeja käytettyään ja palautusarvon saatuaan tekee. View lopettaa tapahtumien käsittelemisen yksityisellä metodillaan, joka on vaikutuksellinen. Perusajatus on, että tuo vaikutuksellinen metodi kutsuu View’n omaa isDone-metodia — ja jos isDone palauttaa true, pysäytetään näkymä.

Yhteenvetoa

  • "Kyllä vai ei" -ilmiöitä kuvataan tietokoneohjelmissa usein Boolen logiikan arvoina "tosi" ja "epätosi", joille on monissa kielissä määritelty oma tietotyyppi.

  • Scalassa Boolen logiikan arvoja edustaa tietotyyppi Boolean. Tämäntyyppisiä arvoja on tasan kaksi: true ja false. Niitä voi käyttää literaaleina Scala-ohjelmakoodissa.

  • Totuusarvoja saadaan myös käyttämällä vertailuoperaattoreita, esimerkiksi vertailemalla, onko tietty lukuarvo pienempi kuin toinen vai ei.

  • Tapa, jolla arvojen yhtäsuuruus määritellään riippuu tietotyypistä. Kyseessä voi olla identiteettivertailu tai samanlaisuusvertailu jonkin ominaisuuden tai ominaisuuksien mukaan.

  • Lukuun liittyviä termejä sanastosivulla: totuusarvo; vertailuoperaattori.

Palaute

Huomaathan, että tämä on henkilökohtainen osio! Vaikka olisit tehnyt lukuun liittyvät tehtävät parin kanssa, täytä palautelomake itse.

Tekijät

Tämän oppimateriaalin kehitystyössä on käytetty apuna tuhansilta opiskelijoilta kerättyä palautetta. Kiitos!

Materiaalin luvut tehtävineen ja viikkokoosteineen on laatinut Juha Sorva.

Liitesivut (sanasto, Scala-kooste, usein kysytyt kysymykset jne.) on kirjoittanut Juha Sorva sikäli kuin sivulla ei ole toisin mainittu.

Tehtävien automaattisen arvioinnin ovat toteuttaneet: (aakkosjärjestyksessä) Riku Autio, Nikolas Drosdek, Joonatan Honkamaa, Antti Immonen, Jaakko Kantojärvi, Niklas Kröger, Kalle Laitinen, Teemu Lehtinen, Jaakko Nakaza, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä, Anna Valldeoriola Cardó ja Aleksi Vartiainen.

Lukujen alkuja koristavat kuvat ja muut vastaavat kuvituskuvat on piirtänyt Christina Lassheikki.

Yksityiskohtaiset animaatiot Scala-ohjelmien suorituksen vaiheista suunnittelivat Juha Sorva ja Teemu Sirkiä. Teemu Sirkiä ja Riku Autio toteuttivat ne apunaan Teemun aiemmin rakentamat työkalut Jsvee ja Kelmu.

Muut diagrammit ja materiaaliin upotetut vuorovaikutteiset esitykset laati Juha Sorva.

O1Library-ohjelmakirjaston ovat kehittäneet Aleksi Lukkarinen ja Juha Sorva. Useat sen keskeisistä osista tukeutuvat Aleksin SMCL-kirjastoon.

Tapa, jolla käytämme O1Libraryn työkaluja (kuten Pic) yksinkertaiseen graafiseen ohjelmointiin, on saanut vaikutteita tekijöiden Flatt, Felleisen, Findler ja Krishnamurthi oppikirjasta How to Design Programs sekä Stephen Blochin oppikirjasta Picturing Programs.

Oppimisalusta A+ luotiin alun perin Aallon LeTech-tutkimusryhmässä pitkälti opiskelijavoimin. Nykyään tätä avoimen lähdekoodin projektia kehittää Tietotekniikan laitoksen opetusteknologiatiimi ja tarjoaa palveluna laitoksen IT-tuki. Pääkehittäjänä on nyt Markku Riekkinen, jonka lisäksi A+:aa ovat kehittäneet kymmenet Aallon opiskelijat ja muut.

A+ Courses -lisäosa, joka tukee A+:aa ja O1-kurssia IntelliJ-ohjelmointiympäristössä, on toinen avoin projekti. Sen suunnitteluun ja toteutukseen on osallistunut useita opiskelijoita yhteistyössä O1-kurssin opettajien kanssa.

Kurssin tämänhetkinen henkilökunta löytyy luvusta 1.1.

Joidenkin lukujen lopuissa on lukukohtaisia lisäyksiä tähän tekijäluetteloon.

a drop of ink
Palautusta lähetetään...