Luku 3.4: Valintoja
Valitsemisen tarve
Mitä erilaisimmissa ohjelmissa on tarpeen tehdä valintoja. Esimerkiksi: Täytyy valita uudeksi suosikkikokemukseksi joko uusi kokemus tai vanha suosikki. Tai: halutaan suorittaa toiminto vain, jos käyttäjä vastaa "kyllä" eikä "ei".
On siis voitava muodostaa ohjelmaan ehtoja: vastasiko käyttäjä "kyllä"; onko kokemus parempi kuin toinen? Ehtojen muodostaminen meiltä jo onnistuukin totuusarvoja käyttäen.
Lisäksi tarvitsemme tavan, jolla voimme määrätä tietyt käskyt suoritettaviksi vain, jos ehto on voimassa. Kätevää olisi sekin, että samalla voisimme määrätä vaihtoehtoiset käskyt suoritettaviksi siinä tapauksessa, että ehto ei toteudu.
Scala tarjoaa useita eri tapoja käskyjen valinnaiseen suorittamiseen. Tässä luvussa
niistä käsitellään suoraviivaisinta eli if
-valintakäskyä.
if
-käsky
Sanoja if
ja else
käyttäen voi muodostaa lausekkeita, joiden arvo riippuu siitä,
toteutuuko tietty ehto evaluointihetkellä eli onko tietyn Boolean
-tyyppisen lausekkeen
arvo true
vai ei. Perusidea on pseudokoodina tämä:
if ehto then arvo ehdon toteutuessa else arvo muutoin
Käskyn toimintaa on helppo tutkia REPLissä:
if 10 > 100 then "on isompi" else "ei ole isompi"res0: String = ei ole isompi if 100 > 10 then "on isompi" else "ei ole isompi"res1: String = on isompi
Ehtolausekkeen on oltava tyyppiä Boolean
, jolloin sen arvoksi
saadaan evaluoitaessa joko true
tai false
. Tässä ehtolauseke
on muodostettu vertailuoperaattorilla. Kun if
-käsky
suoritetaan, evaluoidaan aluksi tämä ehtolauseke kertaalleen
ja sen perusteella määrittyy, mitä seuraavaksi tapahtuu.
Jos ehtolausekkeen arvo on false
, suoritetaan else
-sanan
jälkeinen ohjelmakoodi eli "else
-haara". Koko if
-lausekkeen
arvoksi saadaan else
-haaran arvo.
Jos ehtolausekkeen arvo on true
, suoritetaan heti
ehtolauseketta seuraava ohjelmakoodi eli "then
-haara".
Koko if
-lausekkeen arvoksi saadaan then
-haaran arvo.
Esimerkkejä
Ehtolausekkeena voi olla vertailun sijaan mikä vain Boolean
-tyyppinen lauseke, vaikkapa
Boolean
-muuttujan nimi:
val maapalloOnLittea = falsemaapalloOnLittea: Boolean = false if maapalloOnLittea then "Niin varmaan" else "Ihanko tosi?"res2: String = Ihanko tosi?
Tai totuusarvoliteraali true
tai false
(joskaan tästä ei ole liiemmin hyötyä):
if true then "Tämä valittiin" else "Tätä ei"res3: String = Tämä valittiin
Äskeiset esimerkit valitsivat kahden merkkijonolausekkeen välillä, mutta then
- ja
else
-haaroina voi toki käyttää myös vaikkapa lukuarvoisia lausekkeita:
val luku = 100luku: Int = 100 if luku > 0 then luku * 2 else 0res4: Int = 200 if luku < 0 then luku * 2 else 0res5: Int = 0
Koko if
-lausekkeen arvon tyyppi määräytyy sen mukaan,
mitä valinnaisiin haaroihin kirjoitetaan.
if
-käsky lausekkeena
Valintakäskyllä on siis tyyppi. Tällaista käskyä voi käyttää lausekkeena missä tahansa yhteydessä, johon sen tietotyyppi sopii. Niinpä valintakäskyn voi kirjoittaa vaikkapa parametrilausekkeeksi, tai käskyn tuottaman arvon voi sijoittaa muuttujaan. Jos valintakäsky tuottaa numeron, niin käskyä voi käyttää osana aritmeettista laskutoimitusta. Esimerkiksi seuraavat käskyt ovat mahdollisia:
println(if luku > 100 then 10 else 20)20 val valinnanTulos = if luku > 100 then 10 else 20valinnanTulos: Int = 20 (if luku > 100 then 10 else 20) * (if luku <= 100 then -luku else luku) + 1res6: Int = -1999
Viimeinen noista käskyistä on tosin jo sen verran haarakas, että on selkeämpää ottaa
if
-lausekkeiden arvot tilapäissäilöön muuttujiin ja vasta sitten kertoa ne keskenään.
Tähän tapaan:
val ekaTulos = if luku > 100 then 10 else 20ekaTulos: Int = 20 val tokaTulos = if luku <= 100 then -luku else lukutokaTulos: Int = -100 ekaTulos * tokaTulos + 1res7: Int = -1999
Pikkutehtäviä
if
-käskyn jakaminen riveiksi
Mainittakoon tässä välissä, että jos valintakäskystä muodostuisi muuten pitkä ja hankalalukuinen, niin sen voi jakaa usealle riville esimerkiksi näin:
val pidempiKommentti =
if elain == suosikki then
"Mahtavaa! Se on " + elain + ", joka on oma suosikkini."
else
"Ihan kiva eläinhän se on " + elain + "kin."
end if
Monirivisessä if
-käskyssä then
-haara ja else
-haara
sisennetään syvemmälle kuin if
- ja else
-alkuiset rivit.
Loppuun voi vapaaehtoisesti kirjata loppumerkin. Usein loppumerkki kyllä jätetään pois, mutta sen kirjoittamisessa ei ole mitään ongelmaa, jos koet, että se selkiyttää kyseistä ohjelmaa.
Minitehtävä: kuvan kuvailu, osa 1/2
Kirjoita Miscellaneous-moduulin misc.scala
-tiedostoon
vaikutukseton funktio describe
, joka
ottaa yhden,
Pic
-tyyppisen parametrin, japalauttaa merkkijonon "portrait", jos kuvan korkeus on leveyttä suurempi ja merkkijonon "landscape" muutoin (eli jos kuva on neliön muotoinen tai leveämpi kuin korkea).
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
Experience
-luokka valmiiksi
Voimme käyttää if
-valintakäskyä luokan ohjelmakoodissa. Näin mahdollistamme, että oliot
voivat tehdä valintoja, kun niiden metodeita kutsutaan.
Valitseva chooseBetter
-metodi
Meillä on jo luvusta 3.3 osittainen toteutus Experience
-luokalle. Luokasta jäi tällöin
toteuttamatta chooseBetter
-metodi, joka vertailee kahta kokemusta ja palauttaa niistä
arvosanaltaan paremman. Kunhan saamme metodin toteutettua, sen pitäisi toimia tähän tapaan:
val wine1 = Experience("Il Barco 2001", "ookoo", 6.69, 5)wine1: o1.goodstuff.Experience = o1.goodstuff.Experience@1b101ae val wine2 = Experience("Tollo Rosso", "turhahko", 6.19, 3)wine2: o1.goodstuff.Experience = o1.goodstuff.Experience@233b80 val betterOfTheTwo = wine1.chooseBetter(wine2)betterOfTheTwo: o1.goodstuff.Experience = o1.goodstuff.Experience@1b101ae betterOfTheTwo.nameres8: String = Il Barco 2001
chooseBetter
-metodin ajatus on siis samankaltainen kuin max
-funktion (luku 1.6),
joka palauttaa kahdesta vertailemastaan luvusta suuremman.
Tässä ensin Experience
-toteutus, jossa chooseBetter
on pseudokoodina:
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 def chooseBetter(another: Experience) = Selvitä, onko tämä kokemus arvosanaltaan parempi kuin another-kokemus. Jos oli, palauta viittaus this-olioon, muuten palauta another-muuttujan sisältämä viittaus. end Experience
Ja sitten metodin koodi Scalalla:
def chooseBetter(another: Experience) =
val thisIsBetter = this.rating > another.rating
if thisIsBetter then this else another
this
-sana yksinään muodostaa lausekkeen, jonka arvo on viittaus
metodia suorittavaan olioon itseensä.
chooseBetter
in suoritus päättyy if
-lausekkeeseen; metodi palauttaa
tämän if
-lausekkeen arvon. Se on viittaus metodia suorittavaan olioon,
jos muuttujan thisIsBetter
arvo on true
, tai parametriksi saatu
viittaus, jos thisIsBetter
on false
.
Parempi ratkaisu omaa metodia kutsumalla
Äskeinen toteutus toimii. Ehkä silti haluamme poistaa Experience
-luokasta tarpeettoman
toiston: nythän metodeissa isBetterThan
ja chooseBetter
on täsmälleen sama
vertailuoperaatio this.rating > another.rating
. Kokemusten paremmuus on siis
määritelty kahteen kertaan eri paikoissa.
Tämä ei ole kovin eleganttia ja tekee ohjelmasta hieman hankalamman muokata. Jos esimerkiksi haluaisimme muuttaa ohjelmaa niin, että vertailu tapahtuukin hinta–laatu-suhteen eikä arvosanan perusteella, niin pitäisi muuttaa kahta kohtaa. Tämä ei ole tässä pienessä ohjelmassa kauheaa, mutta isommissa ohjelmissa toisteisuudesta seuraa pian ylläpidettävyysongelmia.
Koetetaan tehdä vähän paremmin.
Olet jo useasti nähnyt, että olio voi "lähettää itselleen viestin" eli kutsua
omaa metodiaan: this.metodi(parametrit)
. Niinpä voimme muotoilla uuden version
chooseBetter
-metodista:
def chooseBetter(another: Experience) =
val thisIsBetter = this.isBetterThan(another)
if thisIsBetter then this else another
Experience
-olio ikään kuin kysyy itseltään: "Oletko parempi
kuin tämä toinen kokemus?"
Nyt chooseBetter
käyttää juuri sitä tapaa vertailla paremmuutta, joka on määritelty
isBetterThan
-metodissa, eikä toistoa ole.
Tiiviimpi ratkaisu: metodikutsu ehtolausekkeena
Varustaudutaan vielä tiedolla, että if
-käskyssä voi käyttää myös metodikutsua
ehtolausekkeena, kunhan vain metodin paluuarvo on tyyppiä Boolean
. Tämä tulee ilmi
seuraavasta REPL-esimerkistä (jossa oletetaan wine1
ja wine2
määritellyiksi kuin
edellä):
if wine1.isBetterThan(wine2) then "oli parempi" else "ei ollut parempi"res9: String = oli parempi if wine2.isBetterThan(wine1) then "oli parempi" else "ei ollut parempi"res10: String = ei ollut parempi if wine1.isBetterThan(wine1) then "oli parempi" else "ei ollut parempi"res11: String = ei ollut parempi
Ehtolausekkeen evaluointi tarkoittaa tässä tapauksessa metodin kutsumista; lausekkeen arvoksi saadaan metodin palauttama totuusarvo. Tämän jälkeen jatketaan paluuarvon mukaiseen haaraan.
Tältä pohjalta voimme lyhentää chooseBetter
-toteutustamme hieman. Paikallista muuttujaa
thisIsBetter
ei välttämättä tarvita:
def chooseBetter(another: Experience) = if this.isBetterThan(another) then this else another
Experience
-luokkamme on valmis. GoodStuff-moduulin toiseen keskeiseen luokkaan Category
tartumme myöhemmin luvussa 4.2.
Odds-tehtävä (osa 7/9)
Palataan taas hetkeksi edellisistä luvuista tuttuun Odds-hankkeeseemme ja lisätään sinne
metodi, jolla Odds
-olion kuvaama tieto voidaan esittää ns. moneyline-muodossa, jota
varsinkin amerikkalaiset vedonlyöntitoimistot käyttävät. Tämä hieman kummallinen
esitysmuoto toimii seuraavasti:
(Alla P ja Q viittaavat lukuihin, joista todennäköisyyden
fractional
-muotoinen esitys P/Q muodostuu.)Jos tapahtuman on arvioitu tapahtuvan korkeintaan 50 %:n todennäköisyydellä, sen moneyline-luku on positiivinen ja saadaan laskemalla 100 ⋅ P ∕ Q. Esimerkiksi tapausta 7/2 vastaa moneyline-luku 350, koska 100 ⋅ 7 ∕ 2 = 350. Tämän positiivisen luvun voi tulkita niin, että panostamalla 100 rahaa vedonlyöjä voi tehdä 350 rahaa voittoa sen lisäksi, että saa panoksensa takaisin. Fifty–fifty-tilannetta 1/1 vastaa moneyline-luku 100.
Jos taas todennäköisyydeksi on arvioitu yli 50 %, moneyline-luku on negatiivinen ja saadaan laskemalla -100 ⋅ Q ∕ P. Esimerkiksi tapausta 1/5 vastaa moneyline-luku -500, koska -100 ⋅ 5 ∕ 1 = -500. Tämän negatiivisen luvun voi tulkita niin, että tehdäkseen 100 rahaa voittoa vedonlyöjän on panostettava 500 rahaa.
Tehtävänanto
Laadi Odds
-luokkaan metodi moneyline
, joka palauttaa Odds
-oliota vastaavan
moneyline-luvun Int
-arvona:
val norwayWin = Odds(5, 2) norwayWin: Odds = o1.odds.Odds@171c36b norwayWin.moneylineres12: Int = 250
Lisää myös käynnistysolioon OddsTest1
rivi, joka tulostaa moneyline-luvun. Ohjelman
tulosteen on nyt näytettävä tällaiselta:
Please enter the odds of an event as two integers on separate lines.
For instance, to enter the odds 5/1 (one in six chance of happening), write 5 and 1 on separate lines.
11
13
The odds you entered are:
In fractional format: 11/13
In decimal format: 1.8461538461538463
In moneyline format: -118
Event probability: 0.5416666666666666
Reverse odds: 13/11
Odds of happening twice: 407/169
Please enter the size of a bet:
200
If successful, the bettor would claim 369.2307692307692
Please enter the odds of a second event as two integers on separate lines.
10
1
The odds of both events happening are: 251/13
The odds of one or both happening are: 110/154
Yksi pieni lisäys riittää.
Ohjeita ja vinkkejä
Käytä
if
-lauseketta.moneyline
-metodin on tässä palautettava kokonaisluku. Jätä desimaaliosa kokonaan pois eli pyöristä aina kohti nollaa. Kokonaislukujen jakolaskuhan tekee tämän puolestasi (luku 1.3), joten helpoimmalla pääset, kun vain teet laskutoimitukset sopivassa järjestyksessä: kerro ensin, jaa sitten.Se on yhdyssana, joten
moneyline
eikämoneyLine
.
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
Apokalyptistä ohjelmointia
Suomalaisessa kansanperinteessä Jumala rankaisi pyytä tuomiten sen vähitellen pienenemään maailman loppuun saakka. Lopun tiedetään olevan liki, kun pyy on häviävän pieni. Tätä perua on sanonta "pienenee kuin pyy maailmanlopun edellä".
Simuloidaan tilanne. Tässä on pyiden mallintamiseen sopiva luokka:
class Pyy:
private var koko = 400
private val peruskuva = Pic("bird.png")
def loppuKoitti = this.koko <= 0
def pienene() =
if this.koko > 0 then
this.koko = this.koko - 1
def kuvaksi = this.peruskuva.scaleTo(this.koko)
end Pyy
Löydät tuon luokan ja sitä käyttävän käyttöliittymän Pikkusovelluksia-moduulista. Käyttöliittymä käyttää luvussa 3.1 opittuja konsteja pienentääkseen valkoista taustaa vasten näkyvää pyytä kunnes se katoaa näkyvistä.
Tehtäväsi on tutustua annettuun koodiin ja muokata käyttöliittymän
makePic
-metodia niin, että se vaihtaa koko kuvan mustaksi lopun
koittaessa. Metodin pitää siis palauttaa
linnun kuva valkoista taustaa vasten (kuten annetussa koodissa) vain silloin, jos pyyn
loppuKoitti
-metodi palauttaafalse
; taitäysmusta
maailmanlopunKuva
, jos tuo metodi palauttaatrue
.
Käytännössä täytyy vain lisätä metodiin yksi if
-lauseke.
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
Tilaan vaikuttaminen if
-käskyssä
if
-käskyn haarat voivat sisältää myös käskyjä, joilla on tilaa muuttavia vaikutuksia
(luku 1.6) kuten tulostamista, muuttujien arvon vaihtamista yms.
Tulostava if
-käsky
if luku > 0 then
println("Luku on positiivinen.")
println("Tarkemmin sanoen se on: " + luku)
else
println("Kyseessä ei ole positiivinen luku.")
println("Olen puhunut.")
Sisennetyssä haarassa voi olla useitakin käskyjä, jotka suoritetaan peräjälkeen, mikäli ehtolausekkeen arvo on sopiva.
Huomaa: Esimerkin viimeinen tulostuskäsky ei kuulu if
-käskyyn
vaan on sen perässä. Vaikka edeltä on loppumerkki end if
jätetty
pois, asia näkyy siitä, että rivi on sisentämätön. Tuo tulostuskäsky
suoritetaan siis ehtolausekkeen arvosta riippumatta, kunhan ensin on
suoritettu jompikumpi yllä olevista "haaroista". (Jos viimeinenkin
rivi olisi sisennetty, se suoritettaisiin vain mikäli luku
ei ole
positiivinen.)
Seuraavat animaatiot kuvaavat esimerkkikoodin toiminnan ensin positiivisella ja sitten negatiivisella lukuarvolla.
Sama yleisemmin
Aiemmin tässä luvussa käytimme if
-käskyä lausekkeiden eli "ilmaisujen, joilla
on arvo" muodostamiseen. Valitsimme if
-käskyllä kahden arvon välillä. Äskeisessä
animoidussa koodissa puolestaan käytettiin if
-käskyä valitsemaan tiettyjen tilaa
muuttavien vaikutusten välillä. Tässä vielä pseudokoodi, joka kuvaa tuon yleisen
ajatuksen:
if ehto then käskyjä, jotka suoritetaan, jos ehtolausekkeen arvo oli tosi else käskyjä, jotka suoritetaan, jos ehtolausekkeen arvo oli epätosi end if // voi jättää pois
Kun if
-käskyä käytetään tilan muuttamiseen, koodi on tapana rivittää ja sisentää
yllä esitetyllä tavalla riippumatta siitä, montako riviä kumpaankin "haaraan" tulee.
Näin teemme tälläkin kurssilla, kuten kurssin tyyliopaskin
kertoo.
Unit
if
-käskyn arvona
Vaikka seuraava sijoituskäsky ei olekaan kovin fiksu, on sitä hyvä pohtia hetki:
val turhaTulos = if luku > 1000 then println("Aika iso") else println("Ei niin iso")Ei niin iso println(turhaTulos)()
Tässä sijoituskäskyn osana käytetyllä if
-käskyllä ei ole merkityksellistä arvoa. Koodi
kyllä tulostaa lukumuuttujan arvon perusteella jommankumman tekstin, mutta se ei sijoita
muuttujaan turhaTulos
mitään järkevää vaan vain sisällöttömän Unit
-arvon (joka näkyy
tulosteessa tyhjinä sulkeina). Näin tapahtuu siksi, että if
-käskyn haarat päättyvät
tulostuskäskyyn, joka ei palauta merkityksellistä arvoa. Tällaista if
-käskyä on siis
turha käyttää muuttujasijoituksessa.
Voi olla sivistävää vielä verrata äskeistä koodia näihin:
val tulos = if luku > 1000 then "Aika iso" else "Ei niin iso"tulos: String = Ei niin iso println(tulos)Ei niin iso
println(if luku > 1000 then "Aika iso" else "Ei niin iso")Ei niin iso
if
ilman else
ä
Kun käytämme if
-käskyä ohjelman tilaan vaikuttamiseen, ei else
-haaraa välttämättä
edes tarvita. Usein haluamme suorittaa jonkin käskyn tai käskyt vain ehdon täyttyessä,
mutta muuten ei tehdä mitään.
Tietysti voisimme tehdä jotain tällaista:
if ehto then käskyjä, jotka suoritetaan, jos ehto oli tosi else () end if
Tyhjä else
-haara ei tee mitään. Mutta sitä ei edes
tarvitse kirjoittaa; voimme jättää kaiken tämän pois.
Jos else
-haaraa ei ole ja ehtolausekkeen arvoksi tulee false
, niin loput if
-käskyn
sisältö (then
-haara) yksinkertaisesti ohitetaan:
if ehto then käskyjä, jotka suoritetaan, jos ehto oli tosi, ja ohitetaan muutoin end if // tämä rivi sopii jättää pois
Esimerkki:
if luku != 0 then
println("Osamäärä on: " + 1000 / luku)
println("Loppu.")
Tämä if
-käsky on vaikutuksellinen (se tulostaa), ja siksi
rivitämme ja sisennämme, vaikka haarassa on vain yksi rivi.
Jälkimmäinen, sisentämätön tulostuskäsky ei kuulu if
-käskyyn
vaan on sen perässä. Tämä koodinpätkä siis tulostaa lopuksi
"Loppu." riippumatta siitä, oliko luku
-muuttujan arvo nolla
vai ei. Mikäli oli, ei tämä koodi muuta tulostakaan.
Voit katsoa tämänkin koodin suorituksesta lyhyen animaation, jos haluat:
Pikkutehtävä
FlappyBug-tehtävä (osa 12/17: hienosäätöä)
Tehtävänanto
Lisää FlappyBug-ohjelmaan kaksi if
-käskyä niin, että:
Ötökkä ponnahtaa ylöspäin vain, jos painettu näppäin on välilyönti, eikä millä tahansa näppäimellä kuten tähän asti.
Ötökän putoamisnopeus kiihtyy (eli
yVelocity
-ilmentymämuuttujan arvo kasvaa) vain, jos ötökkä on ilmassa eli maanpinnan yläpuolella.
Maahan pudottuaan ötökän pitää pystyä edelleen räpyttelemään sieltä ylös.
Maanpinta on samassa y-koordinaatissa 350 kuin tehtäväsarjan aiemmissakin vaiheissa.
Ohjeita ja vinkkejä
Lisäykset tulevat
FlappyBugApp
-ohjelmanonKeyDown
-metodiin jaBug
-luokanfall
-metodiin. Molemmat olivat esillä, kun peliä muokattiin luvussa 3.1.Aivan kuin aiemmissakin Flappy-tehtävissä,
fall
-metodin tulee ensin kasvattaa kiihtyvyyttä (jos maanpinnan yläpuolella ennen liikettä) ja vasta sitten siirtää ötökkää.Tapahtumankäsittelijämetodi
onKeyDown
saa parametriksi tiedon siitä, mitä näppäintä painettiin. Voit verrata tätä arvoa yhtäsuuruusoperaattorilla==
arvoonKey.Space
, joka on välilyöntinäppäintä tarkoittava vakioarvo.Et tarvitse
else
-haaroja.Varo estämästä ötökän liikettä kokonaan sen pudottua maahan. Vain alaspäin kiihdytys lakkaa.
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
if
-käskyjen yhdistelemisestä
Pohjustava koodinlukutehtävä
else if
-ketjutus
Jos haluat valita useammasta kuin kahdesta vaihtoehdosta, voit kirjoittaa if
-käskyn
else
-haaraan toisen if
-käskyn:
val kuvaus = if luku < 0 then "nega" else if luku > 0 then "posi" else "nolla"
Sulkeet voivat selkiyttää käskyä hieman:
val kuvaus = if luku < 0 then "nega" else (if luku > 0 then "posi" else "nolla")
Parhaiten monihaaraisuus ehkä näkyy rivittämällä ja sisentämällä esimerkiksi näin:
val kuvaus =
if luku < 0 then
"nega"
else if luku > 0 then
"posi"
else
"nolla"
Tällainen else if
-ketjuttaminen toki toimii myös, kun valitaan vaikutuksellisten
käskyjen välillä:
if luku < 0 then
println("Luku on negatiivinen.")
else if luku > 0 then
println("Luku on positiivinen.")
else
println("Luku on nolla.")
Minitehtävä: kuvan kuvailu, osa 2/2
Muokkaa ylemmässä minitehtävässä misc.scala
an laatimaasi
describe
-funktiota niin, että se palauttaa
merkkijonon "portrait", jos kuvan korkeus on leveyttä suurempi,
merkkijonon "landscape", jos kuvan leveys on korkeutta suurempi, ja
merkkijonon "square", jos kuvan leveys on prikulleen sama kuin sen korkeus.
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
if
-käskyjä sisäkkäin
if
-käskyjä voi muutenkin kirjoittaa sisäkkäin. Tällöin pitää olla tarkkana sen kanssa,
mihin if
-käskyyn kukin else
-haara liittyy. Tutustu asiaan seuraavan REPL-esimerkin
kautta:
val luku = 100luku: Int = 100 if luku > 0 then println("On positiivinen.") if luku > 1000 then println("On yli tuhat.") else println("On positiivinen muttei yli tuhat.") end if end ifOn positiivinen. On positiivinen muttei yli tuhat.
Luvun ollessa 100 suoritetaan ulomman if
-käskyn sisältö,
johon sisältyy ensimmäinen tulostuskäsky.
Tuohon then
-haaraan sisältyy myös toinen if
-käsky. Koska tämän
sisemmän if
-käskyn ehto ei toteudu, päädytään sen else
-osaan.
Sisemmän if
-käskyn haarat ovat vielä sisennetymmät.
Kun sisennykset ovat kunnossa, if
-sana, else
-sana ja
(valinnainen) loppumerkki ovat pystysuorassa linjassa keskenään.
Tämä pätee sekä sisemmässä...
... että ulommassa valintakäskyssä. Ulommassa käskyssä ei tässä
esimerkissämme ole else
-haaraa ollenkaan. Loppumerkin voisi
jälleen kerran jättää pois, mutta ehkäpä se selkiyttää koodia,
jossa on tällaisia sisäkkäisiä rakenteita.
Äskeisessä esimerkissä siis else
-sana liittyi "sisempään" if
-käskyyn, joka oli
sisennetty sen kanssa samalle tasolle. Tuo else
-osa suoritettiin nimenomaan siksi,
että ulomman käskyn ehto toteutui mutta sisemmän ei. Jos haluamme liittää else
n
ulompaan if
-käskyyn, se käy siirtämällä rivejä ja muuttamalla sisennyksiä:
if luku > 0 then println("On positiivinen.") if luku > 1000 then println("On yli tuhat.") end if else println("On nolla tai negatiivinen.") end ifOn positiivinen.
Nyt ulommalla if
-käskyllä on kaksi haaraa, ja else
-haara
suoritettaisiin vain, jos luku ei olisi positiivinen. Käskyn
kukin avainsana on sisennetty samalle tasolle.
Sisemmällä if
-käskyllä ei tässä esimerkissä ole else
-haaraa
lainkaan. Tulosterivejä tulee vain yksi.
Sisäkkäisyystehtävä
Odds-tehtävä (osa 8/9)
Tehtävänanto
Luvun 3.3 OddsTest2
-käynnistysolio tulostaa eräitä tietoja kahdesta Odds
-oliosta.
Muuta sitä:
Poista
isLikely
- jaisLikelierThan
-metodien paluuarvoja tulostavatprintln
-käskyt. (Siis ne, joissa tuloste alkaa The-sanalla.) Tämä siksi, että tarkoitus on nyt raportoidaOdds
-olioiden tietoja hieman toisin kuin ennen.Nyt laadittavan ohjelman on tutkittava, onko ensimmäinen syötetty
Odds
todennäköisempi kuin toinen ja tulostettava sen perusteella jompikumpi seuraavista lauseista: The first event is likelier than the second. tai The first event is not likelier than the second.Tämän jälkeen ohjelman on tulostettava Each of the events is odds-on to happen., mikäli molemmat tapahtumat ovat todennäköisiä (eli
isLikely
). Jos kumpi tahansa tapahtumista ei ole todennäköinen, ei tässä kohtaa tulosteta mitään.Kuten aiemmin määrittelimme, tapahtuma on todennäköinen, jos se toteutuu luultavammin kuin ei toteudu eli jos sen
isLikely
-metodi palauttaatrue
.
Thank you -alkuisen lopputervehdyksen on edelleen tulostuttava kaikissa tapauksissa.
Ohjelman tulosteen pitää siis vastata täsmälleen näitä ajoesimerkkejä:
Please enter the odds of the first event as two integers on separate lines. 5 1 Please enter the odds of the second event as two integers on separate lines. 1 2 The first event is not likelier than the second. Thank you for using OddsTest2. Please come back often. Have a nice day!
Please enter the odds of the first event as two integers on separate lines. 1 1 Please enter the odds of the second event as two integers on separate lines. 2 1 The first event is likelier than the second. Thank you for using OddsTest2. Please come back often. Have a nice day!
Please enter the odds of the first event as two integers on separate lines. 1 2 Please enter the odds of the second event as two integers on separate lines. 2 3 The first event is likelier than the second. Each of the events is odds-on to happen. Thank you for using OddsTest2. Please come back often. Have a nice day!
Ohjeita ja vinkkejä
Käytä
if
-käskyjä.Selvittäessäsi, ovatko molemmat tapahtumista todennäköisiä, voit kirjoittaa kaksi
if
-käskyä sisäkkäin. Vaihtoehtoisesti voit kurkistaa jo lukuun 5.1 ja noukkia sieltä toisen keinon kahden ehdon yhdistämiseen.
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
Poissulkevuudesta, tyhjentävyydestä ja tyylistäkin
Päätetään luku koodinlukuharjoitukseen, joka nostaa esiin myös ohjelmointityyliin liittyviä asioita. (Niin, ja tästä voi muuten kuitata itselleen ensimmäiset B-pisteet.)
Alla olevat kysymykset kytkeytyvät raportoi
-nimiseen funktioon, jonka tulisi tutkia
annettua lukua ja muodostaa rivi tulostetta sen perusteella. Tässä esimerkki halutuista
tulosteista REPLissä:
raportoi(40)Aikuinen raportoi(80)Vanhus raportoi(15)Lapsi
Funktio on siis vaikutuksellinen, ja sen pitäisi tulostaa täsmälleen yksi seuraavista:
"Aikuinen" mikäli luku on 18 tai yli mutta alle 70.
"Vanhus" mikäli luku on 70 tai yli.
"Lapsi" mikäli luku on alle 18. (Myös jos luku on negatiivinen; ei välitetä nyt siitä.)
Vielä noista raportoi
-versioista
Äskeisissä kysymyksissä otettiin annettuna, että joka haarassa oli
println
-käsky ja if
-käskymme olivat siis vaikutuksellisia. Näin
ei tarvitsisi olla. Alkuperäisen funktiomme voisi mainiosti toteuttaa
vaikutuksettomalla if
-lausekkeella, joka valitsee arvokseen merkkijonon
"Vanhus", "Aikuinen" tai "Lapsi". Tämän jälkeen tarvitaan vain yksi
println
-käsky, joka tulostaa valitun merkkijonon.
Osaatko itse muotoilla tuollaisen version koodista?
Tällainen ratkaisu erottaa siististi toisistaan funktion kaksi
tehtävää: valitsemisen ja tulostamisen. Se on myös aavistuksen
enemmän DRY, koska tuloksen raportoivaa println
-käskyä ei
kirjoiteta useasti.
Yhteenvetoa
if
-valintakäskyllä voi valita kumpi kahdesta käskystä tai käskyjen sarjasta suoritetaan.Valintakäskyllä voi myös määrätä tiettyjä osia ohjelmakoodista valinnaisiksi. Ne suoritetaan vain, jos tietty ehto on voimassa.
Mitä tahansa
Boolean
-tyyppistä lauseketta voi käyttää valintakäskyn ehtona.if
-käskyjä voi yhdistellä laittamalla niitä peräkkäin tai sisäkkäin.Yhdistä käskyt huolella varmistaaksesi paitsi oikean toiminnan myös koodin helppolukuisuuden. Tässä
else
-haarat ovat suureksi avuksi.
Lukuun liittyviä termejä sanastosivulla:
if
; totuusarvo; lauseke; DRY.
Lisätehtävä: tarkkuuspeli
Kirjoita kokonaan uusi ohjelma, jossa käyttäjän pitää yrittää osua hiiren klikkauksella keskelle ikkunaa. Tämä yksinkertainen peli päättyy, jos käyttäjä onnistuu näpäyttämään esim. kolmen pikselin etäisyydelle ikkunan keskikohdasta; tällöin ruudulle ilmestyy jokin valitsemasi kuva merkiksi onnistumisesta.
Voit luoda aihealueen malliksi yksinkertaisen olion, joka pitää
Boolean
-tyyppisessä muuttujassa kirjaa siitä, onko peli päättynyt.
Tarvitset myös käyttöliittymän, joka tekee pelitilanteen näkyväksi.
Tästä vapaamuotoisesta lisätehtävästä ei ole tarjolla automaattista palautetta.
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, Kaisa Ek, Joonatan Honkamaa, Antti Immonen, Jaakko Kantojärvi, Onni Komulainen, Niklas Kröger, Kalle Laitinen, Teemu Lehtinen, Mikael Lenander, Ilona Ma, Jaakko Nakaza, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä, Joel Toppinen, 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, Juha Sorva ja Jaakko Nakaza. 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; sitä 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.
Huomaa avainsanat
if
,then
jaelse
. Ehtolauseke onif
- jathen
-sanojen välissä.