Luku 1.4: Arvojen tallentaminen muuttujiin
Johdanto: välitulosongelma
Miten käyttäisit edellisestä luvusta tuttuja aritmeettisia lausekkeita laskeaksesi Scalalla seuraavat?
kuutosen kuution, siis 63
kuutosen kertoman, siis 6!
kuutosen kertoman kuution, siis 6!3
Eräs ratkaisu REPLissä on annettu alla.
6 * 6 * 6res0: Int = 216 1 * 2 * 3 * 4 * 5 * 6res1: Int = 720
Kaksi ensimmäistä — kuutosen kuutio ja kuutosen kertoma — menivät ihan siedettävästi näinkin. Entä kolmas, kertoman kuutio?
Aargh:
1 * 2 * 3 * 4 * 5 * 6 * 1 * 2 * 3 * 4 * 5 * 6 * 1 * 2 * 3 * 4 * 5 * 6res2: Int = 373248000
Tämä koodinpätkä on jo aika epämiellyttävä kirjoittaa ja lukea. Lisäksi kone joutuu nyt tekemään turhankin monta kertolaskua, kun se suorittaa käskyn (millä ei tosin tässä ole käytännön merkitystä).
Olisi parempi, jos voisi kirjoittaa: "Laske kertoma, ota välitulos talteen ja laske sen kuutio." Ja voikin.
Muuttujat
Lähes kaikissa ohjelmissa on tarvetta tallentaa tietoja koneen muistiin. Näin voidaan pitää kirjaa ohjelman kannalta oleellisista asioista, kuten välituloksista tai vaikkapa GoodStuff-ohjelman tapauksessa käyttäjän kokemusten saamista arvosanoista ja hinnoista.
Tietysti tallennettuihin arvoihin pitäisi päästä myös jotenkin käsiksi. Parhaiten tämä onnistuu käyttämällä nimiä, joilla arvoihin voi viitata.
Ohjelmoija voi tallentaa arvoja muuttujiin (variable). Muuttuja on nimetty varastointipaikka yhdelle arvolle. Käskyä, jolla arvo tallennetaan muuttujaan, sanotaan sijoituskäskyksi (assignment):
Välituloksen sijoittaminen muuttujaan
Tässä parempi tapa ratkaista "kuutosen kertoman kuutio" -ongelma.
Määritellään ensin muuttuja, johon otetaan välitulos talteen. Se onnistuu tällaisella käskyllä. Muista taas, että voit siirtää hiiren kursorin vihreiden laatikkojen ylle nähdäksesi, mihin selitykset liittyvät.
val kertoma = 1 * 2 * 3 * 4 * 5 * 6
... ohjelmoijan valitsema muuttujan nimi,
tässä kertoma
, jonka perään tulee
yhtäsuuruusmerkki...
... ja viimeisenä lauseke, jonka evaluointi tuottaa muuttujaan sijoitettavan arvon.
REPL vastaa muuttujan määrittelyyn:
val kertoma = 1 * 2 * 3 * 4 * 5 * 6kertoma: Int = 720
REPL kuittaa muuttujamäärittelyn laittamalla
uudelle muuttujalle valitsemasi nimen
(automaattisesti numeroidun resX
:n sijaan)
sekä...
... muuttujan tietotyypin ja muuttujaan tallennetun arvon. Scalassa sekä muuttujilla että arvoilla on tietotyypit, ja muuttujan tietotyypin on oltava yhteensopiva muuttujaan tallennetun arvon tietotyypin kanssa.
Nyt voimme käyttää muuttujaa, kun laskemme kertoman kuution:
kertoma * kertoma * kertomares3: Int = 373248000
Huomaa, että muuttujan nimi yksinäänkin toimii lausekkeena; tuon lausekkeen arvo on kyseiseen muuttujaan tallennettu arvo. Muuttujan nimeä, kuten muitakin lausekkeita, voi käyttää suurempien lausekkeiden muodostamiseen. Esimerkiksi tässä muuttujan nimeä on käytetty (kolmeenkin kertaan) osalausekkeena aritmeettisessa lausekkeessa.
Muuttujaan sijoittamisen vaiheet
Alla on vielä animaatio yllä kuvailtujen koodirivien suorituksesta. Katso se, vaikka olisitkin jo mielestäsi tajunnut yllä olevan esimerkin! Kiinnitä erityistä huomiota järjestykseen, jossa suoritus etenee vaiheittain. Näiden vaiheiden järjestys on keskeinen, kun käsittelemämme ohjelmat mutkistuvat.
Varo tutunlaista matikkaa!
Ohjelmakoodi muistuttaa joiltain osin tuttuja matemaattisia merkintätapoja. Tuo samankaltaisuus on harhauttanut monta ohjelmoinnin aloittelijaa, koska aivan samasta asiasta ei ole kyse.
Esimerkki tästä on sijoituskäskyssä. Sijoituskäsky ei ole yhtälö, jossa yhtäsuuruusmerkin molemmat puolet ovat samanarvoisessa asemassa. Sen sijaan se ohjeistaa tietokonetta laskemaan oikeanpuolisen lausekkeen arvon ja tallentamaan tuloksen vasemmalla puolella nimettyyn muistipaikkaan.
Koodin laatu parani
Tässä kaksi edellä laatimaamme ohjelmaa:
1 * 2 * 3 * 4 * 5 * 6 * 1 * 2 * 3 * 4 * 5 * 6 * 1 * 2 * 3 * 4 * 5 * 6res4: Int = 373248000
val kertoma = 1 * 2 * 3 * 4 * 5 * 6 kertoma * kertoma * kertomares5: Int = 373248000
Uusi muuttujaa käyttävä versiomme ohjelmakoodista ratkaisee aivan saman ongelman kuin alkuperäinen yksirivinenkin. Kuitenkin uusi koodimme on ihmiselle selkeämpi lukea. Toinen parannus on (periaatteessa) se, että kone joutuu laskemaan hieman vähemmän kuin edellisessä versiossa. Tämä oli siis ensisilmäys kahteen ohjelman laatukriteeriin: tyyliin ja suoritustehokkuuteen.
Parannetussa koodissamme voi ainakin periaatteessa nähdä jo kolmannenkin laatuparannuksen: vähensimme saman ilmaisun toistoa eriyttämällä kertoman laskemisen omaksi toimenpiteekseen. Toiston vähentäminen helpottaa ohjelman kehittämistä ja muuntamista. Esimerkiksi jos haluaisit muokata ohjelman laskemaan vaikkapa luvun kahdeksan kertoman kuution, niin muutos on helpompi tehdä toiseen, muuttujalliseen ratkaisuumme kuin alkuperäiseen versioon: ei tarvitse muuttaa kolmea kohtaa koodista vaan vain yhtä. Tämä ei ainoastaan vähennä vaivaa vaan myös huolimattomuusvirheiden mahdollisuutta. Tässä erittäin pienessä ohjelmassamme tämäkin parannus on toki käytännön merkitykseltään vähäinen.
Toisteisen koodin välttämisen periaate kulkee nimellä DRY (sanoista don’t repeat yourself); jotkut käyttävät periaatteen rikkomisesta ilmaisua WETWET (write everything twice write everything twice). Isommissa ohjelmissa DRY-periaatetta on tärkeä noudattaa, ja siihen palaamme kurssilla myöhemmin. Tässä vaiheessa riittää saada muhimaan tausta-ajatus siitä, että ohjelmoijan kuuluu arvioida pelkän toimivuuden lisäksi myös laatuseikkoja.
Muuttujien nimet lausekkeina ja lausekkeissa
Erityyppisiä muuttujia
Kaikki yllä käytetyt muuttujat olivat Int
-tyyppisiä, ja niillä oli arvoinaan kokonaislukuja.
Voimme määritellä myös muuntyyppisiä muuttujia. Se onnistuu yksinkertaisimmin sijoittamalla
muuttujaan toisentyyppinen arvo, vaikkapa Double
-tyyppinen luku:
val arvosana = 9.5arvosana: Double = 9.5
Tai merkkijono:
val nimi = "Satu"nimi: String = Satu val nooaAlku = "cccedddfeeddc---"nooaAlku: String = cccedddfeeddc---
Tai väri tai kuva, kuten seuraavassa tehtävässä.
Muuttujien nimeämisestä
Ohjelmoija valitsee muuttujien nimet eli virallisemmin sanoen niiden tunnukset (identifier). Kuten edeltä on käynyt ilmi, Scala-muuttujien nimet aloitetaan yleensä pienellä kirjaimella. Mitään teknistä pakotetta pienen alkukirjaimen käyttöön ei ole, mutta tämän tavan noudattaminen kuuluu hyvään ohjelmointityyliin.
Jos muuttujan nimessä on useita sanoja, jäljempien sanojen aluissa on tapana käyttää isoja kirjaimia. Tässä esimerkkejä:
munHienoMuuttuja
numberOfPlayers
xCoordinate
Tämä Scalassa käytetty nimeämistapa, joka on ohjelmoinnissa melko yleinen muttei mitenkään ainoa, kulkee nimellä camelCase.
Älä käytä muuttujien nimissä välilyöntejä. Numeroita saa käyttää, mutta nimi ei voi
alkaa numerolla. Erikoismerkkejä (+
, &
jne.) on aloittelijan syytä välttää nimissä
kokonaan; joillakin merkeillä on myös erityismerkityksiä Scalassa. Skandinaavisia
"ääkkösiä" ja eksoottisempia kirjoitusmerkkejä voi periaatteessa käyttää, mutta koska
ne voivat tuottaa ongelmia joidenkin ohjelmoijan aputyökalujen kanssa, saattaa olla
parempi välttää niitä. Scala-kielen "taikasanoja" kuten val
, virallisemmin varattuja
sanoja (reserved words), ei voi käyttää muuttujien niminä.
Isot ja pienet kirjaimet lasketaan eri merkeiksi. Kun siis teet muuttujan omaKokeilu
,
niin ole tarkkana, että kirjoitat nimen aina samassa muodossa. Nimi omakokeilu
ei
toimi tuon muuttujan käyttämiseen.
Kurssimateriaalin pienimmissä esimerkeissä on muuttujat ja muut ohjelman osat useimmiten nimetty suomeksi, jotta korostuisi, mitkä sanat ovat ohjelmoijan itse valitsemia ja mitkä ovat osa (englannista sanoja lainaavaa) Scala-ohjelmointikieltä. Kurssin isommissa oheismoduuleissa taas on yleensä käytetty englantia. Itse voit tehdä tältä osin niin kuin parhaaksi näet. Yritysten ohjelmistohankkeissa yleensä käytetään englantia, ja monet suomenkielisetkin ohjelmoijat käyttävät englanninkielisiä nimiä myös henkilökohtaisissa projekteissaan.
Hyvään ohjelmointityyliin kuuluu muuttujien nimeäminen käyttötarkoitusta kuvaavasti.
Tästä näet paljon esimerkkejä jatkossa. Pikkuisissa kokeilukoodinpätkissä toki voi käyttää
yleisluontoisempia ja lyhyempiä nimiä kuten luku
, a
tai vaikka kokeilu
.
Nimeämistä käsitellään myös kurssin tyylioppaassa. Tutustu oppaaseen jossain vaiheessa kurssin alkupuolella, ei kuitenkaan välttämättä vielä nyt ihan alussa.
Muuttujia ja merkkijonoja
Arvojen upottaminen merkkijonoon
Käytetään tätä muuttujaa:
val ikavuodet = 20ikavuodet: Int = 20
Mitä, jos haluaisimme tuottaa tekstin, joka kuvailee muuttujaan tallennetun tiedon? Teksti voisi olla vaikkapa muotoa "Asiakas on X vuotta vanha.", missä X korvataan muuttujaan tallennetulla arvolla. (Tarve on yleinen. Lukemattomat sovellusohjelmat raportoivat samaan tyyliin muistiin tallennettuja tietoja.)
Tässä yksi tapa:
s"Asiakas on $ikavuodet vuotta vanha."res6: String = Asiakas on 20 vuotta vanha.
Huomaa ensimmäistä lainausmerkkiä edeltävä s
-kirjain. Se toimii
merkkinä siitä, että upotamme merkkijonoliteraaliin lausekkeen
arvon. (Englanniksi moinen tunnetaan nimellä string interpolation.)
Merkkijonoon voi kirjoittaa dollarimerkin $
ja sen perään nimen.
Nimi korvautuu vastaavalla arvolla. Tämä toimii, kunhan alussa oli
se s
-kirjain. (Jos s
unohtuu, mitä tapahtuu? Arvaa ja kokeile
REPLissä, menikö oikein.)
Tulos on siis nimenomaan merkkijono: tekstinpätkä, johon luvut sisältyvät numeroita vastaavina kirjoitusmerkkeinä, eivät lukuarvoina.
Alla on lisäesimerkkejä merkkijonoupotuksista.
val luku = 10luku: Int = 10 s"Luku kahdesti ja välilyöntejä välissä: $luku $luku"res7: String = Luku kahdesti ja välilyöntejä välissä: 10 10
Merkkijonoon voi upottaa useitakin lausekkeita. Yllä upotettiin kaksi kertaa sama lauseke, alla kaksi eri lauseketta.
s"$luku on vähän pienempi kuin ${luku + 1}."res8: String = 10 on vähän pienempi kuin 11.
Upotettavan lausekkeen ei tarvitse olla vain muuttujan nimi, vaan
monimutkaisempikin lauseke kelpaa. Tällöin lauseke on rajattava
aaltosulkeilla, kuten juuri tuossa yllä on rajattu aritmeettinen
lauseke luku + 1
. (Jos aaltosulkeet unohtuvat, mitä tapahtuu? Arvaa
ja kokeile REPLissä, menikö oikein.)
Äskeisissä esimerkeissä merkkijonoon upotettiin kokonaislukuja
kuvaavia merkkejä, mutta sama toimii toki muunkintyyppisille
arvoille, kuten Double
ille:
val arvosana = 9.5arvosana: Double = 9.5 val raportti = s"arvosana: $arvosana"raportti: String = arvosana: 9.5 s"$arvosana on saamasi arvosana"res9: String = 9.5 on saamasi arvosana
Merkkijonon sisään voi upottaa String
-tyyppisiäkin arvoja:
val nimi = "Satu"nimi: String = Satu println(s"$nimi, $raportti")Satu, arvosana: 9.5
Tuo viimeinen käsky siis tekee saman kuin tämä merkkijonoja plus-operaattorilla peräkkäin laittava käsky:
println(nimi + ", " + raportti)Satu, arvosana: 9.5
Lisää merkkijonojen plus-operaatiosta
Olemme nähneet, että merkkijonoja saa peräkkäin plus-operaattorilla. Voit yhdistää
merkkijonoon plussalla myös muunlaisen arvon kuten luvun. Lopputulos on sama kuin
merkkijonoupotuksessa. Alla on muutama yllä käytetyistä esimerkeistä kirjoitettuna
plus-operaattorilla merkkijonoupotuksen (eli s
n ja dollarimerkin) sijaan.
"Asiakas on " + ikavuodet + " vuotta vanha."res10: String = Asiakas on 20 vuotta vanha. "Luku kahdesti ja välilyöntejä välissä: " + luku + " " + lukures11: String = Luku kahdesti ja välilyöntejä välissä: 10 10 val raportti = "arvosana: " + arvosanaraportti: String = arvosana: 9.5
Käytännön Scala-ohjelmissa muodostetaan merkkijonoja sekä plus-merkillä yhdistäen että dollarimerkillä upottaen. Niinpä molemmat tavat on syytä tuntea, ja tässä oppimateriaalissakin käytetään molempia.
Plus-operaattorin käyttöön liittyy kuitenkin Scalassa eräs rajoitus, joka pitää huomata. Verrataan kahta koodinpätkää:
s"$arvosana on saamasi arvosana"res12: String = 9.5 on saamasi arvosana arvosana + " on saamasi arvosana"-- Deprecation Warning: |arvosana + " on saamasi arvosana" |^^^^^^^^^^ |... Adding a number and a String is deprecated. Use the string interpolation `s"$num$str"`
Ensimmäinen käsky toimii halutusti merkkijonoupotuksella. Äkkiseltään voisi luulla, että toinen käsky toimisi yhtä hyvin. Kuitenkin saamme varoitusilmoituksen: Scala-kalustomme varoittaa, että noin ei sovi tehdä.
Merkkijonon ja luvun peräkkäin liittävä plus-operaatio on määritelty Scalassa näin: "yhdistä plussan vasemmalla puolella olevan merkkijonon perään oikealla oleva luku". Toisin päin — eli luku vasemmalla ja merkkijono oikealla — ei käykään.
Älä siis käytä tuollaisessa tapauksessa plus-operaattoria. Voit käyttää sen sijaan merkkijonoupotusta, kuten varoitusilmoituskin vihjaa.
Lisää vaihtoehtoja äskeisille
(Tämä ei ole tässä kohden tärkeää, mutta kiinnostanee joitakin kurssilaisia. Voit ohittaa tämän ilman tunnontuskia.)
On vielä muitakin tapoja muodostaa merkkijonoja. Esimerkiksi kaikki seuraavat käskyt tuottavat saman tuloksen:
s"$arvosana on saamasi arvosana"res13: String = 9.5 on saamasi arvosana "" + arvosana + " on saamasi arvosana"res14: String = 9.5 on saamasi arvosana arvosana.toString + " on saamasi arvosana"res15: String = 9.5 on saamasi arvosana
Merkintä ""
on tyhjä merkkijono (empty string)
eli nollan merkin mittainen merkkijono. Kun siihen
yhdistetään luku, saadaan tuon luvun kuvaava merkkijono
(johon voi sitten edelleen yhdistellä muita arvoja).
Lisää käyttöä tyhjille merkkijonoille löytyy mm.
luvussa 4.1.
Vauhtia musiikkiin
Käytetään taas luvun 1.3 esittelemää play
-käskyä. Muodostetaan ensin hieman pidempi
melodia muuttujia apuna käyttäen. Ukko Nooassa alku toistuu uudestaan kappaleen lopussa,
mikä järjestyy esimerkiksi näin:
val nooaAlku = "cccedddfeeddc---"nooaAlku: String = cccedddfeeddc--- val valiosa = "eeeeg-f-ddddf-e-"valiosa: String = eeeeg-f-ddddf-e- val kokoNooa = nooaAlku + valiosa + nooaAlkukokoNooa: String = cccedddfeeddc---eeeeg-f-ddddf-e-cccedddfeeddc--- play(kokoNooa)
Kokeillaan nyt kappaleemme soittamista kahdella eri tempolla. Tallennetaan ensin käyttämämme tempot muuttujiin, joilla on kuvaavat nimet:
val normaaliTempo = 120normaaliTempo: Int = 120 val hidasTempo = 60hidasTempo: Int = 60
play
pystyy soittamaan melodioita eri nopeuksilla. Tämän merkiksi sille
on annettava merkkijono, jossa on nuotteja kuvaavien kirjaimien perässä
kauttaviiva ja edelleen sen perässä haluttua tempoa kuvaavat numeromerkit.
Esimerkiksi tällainen:
kokoNooa + "/" + normaaliTempores16: String = cccedddfeeddc---eeeeg-f-ddddf-e-cccedddfeeddc---/120
Juuri tuollaista kauttaviivallista merkkijonoa play
osaa käsitellä.
Tässä pari esimerkkiä kokeiltavaksi:
play(kokoNooa + "/" + normaaliTempo)play(kokoNooa + "/" + hidasTempo)
120 sattuu olemaan juuri play
-käskyn oletustempo, joten
ensimmäinen soittokerta kuulostaa ihan samalta kuin aiemmatkin.
Kun käytetään tempoa 60, Nooan saunaretki sujuu ikämiehelle sopivampaan tahtiin.
Jos muuttujille ei ole muuta tarvetta, niin koko merkkijonon voi toki kirjata tempoineen kerrallakin kuten alla.
play("cdefg/140")
Muuttujan arvon vaihtaminen
Ohjelmissa on usein tilanteita, joissa jokin tieto muuttuu. Esimerkiksi pelissä pelihahmon sijaintikoordinaatti voi muuttua hahmon liikkuessa, ja GoodStuff-sovelluksessamme käyttäjän suosikkihotelli voi vaihtua toiseen, kun hän lisää tietoja sovellukseen.
Yksi tapa vastata tällaisiin tarpeisiin on pitää kirjaa vaihtuvasta tiedosta muuttujassa ja vaihtaa muuttujan arvo toiseen tilanteen niin vaatiessa.
Kuulostaa aika hyvältä, mutta tämäpä ei val
-sanaa käyttäen määritellyillä muuttujilla
onnistukaan! val
-muuttujan arvo on "lukittu" muuttujaan, eikä sitä voi vaihtaa toiseksi.
var
-muuttujat
Scala-kielen sanalla var
(englannin sanasta variable) voi luoda muuttujan, johon
sijoitetaan arvo kuten val
-muuttujaankin. Ainoana mutta tärkeänä erona val
- ja
var
-muuttujan välillä on se, että jälkimmäisen arvoa voi vaihtaa toiseksi myöhemmin.
Tutustu seuraavaan animaatioon huolellisesti.
Tässä esimerkissämme siis lopulta vaihdettiin sekä luku
- että tupla
-muuttujan arvoa.
Tämä edellyttää, että muuttujat on määritelty var
-sanalla. Jos vaihtaisit var
-sanat
val
eiksi, niin yllä käytetyt koodirivit eivät toimisikaan, vaan saisit virheilmoituksen
error: reassignment to val.
Kenties hämäävä REPL-juttu
Itse asiassa REPL sallii sinun määritellä olemassa olevan nimisiä
muuttujia uudestaan kirjoittamalla uuden var
- tai val
-alkuisen
alustuksen samalle muuttujanimelle. Jos teet näin, saattaa vaikuttaa
siltä, että val
-muuttujankin arvoa voisi vaihtaa. Oikeastaan kyse
on kuitenkin siitä, että uusi määrittely peittää aikaisemman.
Tämä on Scala REPLin erityispiirre, eikä REPLin ulkopuolisissa Scala-ohjelmissa voi tällä tavoin peräkkäisillä käskyillä luoda keskenään samannimisiä muuttujia. Joten kannattaa unohtaa tämä tekniikka.
Toinen esimerkki
Kun var
-muuttujan arvoa muuttaa, voi sen vanhaa arvoa käyttää apuna uutta arvoa määrittäessä:
Varo matikkaa! (taas)
Huomaa ja muista: Matematiikassa muuttuja on symboli, joka vastaa jotakin arvoa. Ohjelmoinnissa muuttuja on nimetty muistipaikka, johon voi tallentaa arvon.
Eron käytännön merkitys korostuu var
-muuttujien kohdalla. Ohjelma
ei ole yhtälöryhmä! Samassa ohjelmassa voi hyvin esiintyä esimerkiksi
sijoitukset luku = 10
sekä luku = 5
. Myös tutusta matemaattisesta
näkökulmasta omituiselta vaikuttava luku = luku + 10
on mahdollinen.
Ja sijoituskäskyjen järjestyksellä on väliä!
Miksi val
?
Protesti! Miksi ikinä tekisin val
-muuttujan? Eikö var
illa voi tehdä kaikki samat ja
enemmän?
var
-muuttujat tosiaan lisäävät mahdollisuuksia, mutta se ei ole pelkästään hyvä puoli.
Ohjelmoijan on järkeiltävä koodinsa toiminnasta koodia kirjoittaessaan ja siitä virheitä
etsiessään. Järkeilyä helpottaa se, että ohjelmoija tietää tiettyjen asioiden koodissa
olevan muuttumattomia. Hän voi esimerkiksi nähdä val
-sanasta suoraan, että tietyn
muuttujan arvo ei missään ohjelman suorituksen vaiheessa muutu miksikään. Tämä seikka
alkaa tuntua merkityksellisemmältä, kunhan kohtaat suurempia ja monimutkaisempia ohjelmia;
huomannet val
-muuttujien edun itsekin vielä tämän kurssin aikana.
Pienissä REPL-kokeiluissa ei ole niin väliä, kummanlaisia muuttujia käytät, mutta tässä jo valmiiksi nyrkkisääntö tulevia ohjelmointitehtäviä varten:
Tee jokaisesta muuttujasta
val
, ellei sinulla ole juuri nyt selvää syytä tehdä siitävar
.
Älä tee var
-muuttujia "varmuuden vuoksi, jos sitä arvoa vaikka tarvitsisi muuttaa". Se
on heikkoa ohjelmointityyliä. Jos myöhemmin osoittautuu, että val
ei sovi tarkoitukseen,
voit vaihtaa var
iin.
Onko val
it edes mitään "muuttujia"?
Voidaan ajatella, että vain var
-muuttujat ovat niitä varsinaisia
muuttujia, koska niiden arvoa voi muuttaa luomisen jälkeenkin.
Silti myös val
-muuttujaa voi perustellusti kutsua "muuttujaksi",
muun muassa siksi, että se voi saada eri arvoja ohjelman eri
suorituskerroilla (vaikkapa käyttäjän syötteestä) ja siksi, että
sama val
-muuttujan luontikäsky saatetaan suorittaa ohjelman aikana
useita kertoja niin, että joka kerta luodaan eriarvoinen val
-muuttuja
edellisen tilalle. Näistä tulee esimerkkejä myöhemmin kurssilla.
val
-muuttujat ovat myös lähempänä matemaattisen muuttujan käsitettä
kuin var
it, ja onpa esitetty sellaistakin, että juuri val
-muuttujat
niitä oikeita muuttujia ovatkin, kun taas var
it ovat "sijoituspaikkoja"
("assignables"). Jätetään tämä sanasota nyt kuitenkin tähän.
Funktionaalinen ohjelmointi
On olemassa laaja ohjelmoinnin suuntaus, funktionaalinen ohjelmointi,
jossa puhtaimmillaan käytetään ainoastaan val
-tyylisiä muuttujia.
Aiheeseen tutustutaan tämän oppimateriaalin luvussa 11.2 ja syvemmin
jatkokursseilla.
Kysyttyä: onko val
illa ja var
illa eroa muistinkäytön tai muun suorituskyvyn kannalta?
Pohjimmiltaan eroa ei ole. Muuttujille varatun muistin määrä riippuu vain tietotyypistä; siitä luvussa 5.4.
Käytännössä asia on monimutkaisempi, ja valinta var
in ja val
in
välillä voi vaikuttaa esimerkiksi niihin optimointeihin, joita
kääntäjät tekevät muokatessaan ohjelmoijan kirjoittaman Scala-koodin
suoritettavaan muotoon. Huomionarvoista on esimerkiksi se, että
val
-muuttujista voi olla etua, kun ohjelmia tehostetaan jakamalla
suorituksen osia eri tahojen rinnakkain suoritettavaksi. Tuota teemaa
ei tällä alkeiskurssilla syvemmin käsitellä.
var
-tehtäviä ja soittimia
play
ja MIDI-äänisynteesi
Soittokäskyn play
tuntemat soittimet on määritelty General
MIDI -standardissa, jonka nimessä MIDI on lyhenne sanoista
Musical Instrument Digital Interface eli digitaalinen
soitinrajapinta. MIDI-pohjaisesti voi tuottaa synteettistä ääntä
erilaisilla virtuaalisoittimilla; tulosten laatu vaihtelee.
Soittokäskymme tarjoaa merkkijonoihin perustuvan, yksinkertaiseen
käyttöön sopivan tavan hyödyntää MIDI-äänisynteesin keskeisimpiä
osia.
Numeroitu soitinluettelo löytyy midi.org -sivustolta.
Tällä kurssilla käytämme MIDI-ääntä "huviksemme", lähinnä eräissä kurssin alkupuolen merkkijonoesimerkeissä. Kuvaamme merkkijonoilla nuotteja, emme suoranaisesti ääntä. Äänen digitaalisesta kuvaamisesta ja nauhoitetusta äänestä tulee puhetta esimerkiksi kurssilla Ohjelmointistudio 1.
play
ja pisteet
Äsken käytetyssä peikkotanssimerkkijonossa oli pisteitä.
play
-käsky tulkitsee pisteitä edeltävät nuotit teräväksi
staccato-soitoksi, jossa nuotti soitetaan lyhyempänä ja
äänen perässä on tauko.
var
ja tietotyypit
Muuttujan tietotyyppi määrää, millaisia arvoja voit sijoittaa muuttujaan. Se ei muutu, ei
var
-muuttujankaan tapauksessa. Esimerkiksi String
-tyyppiseen muuttujaan voi sijoittaa
vain String
-arvoja, kuten seuraava esimerkki osoittaa.
var titteli = "opiskelija"titteli: String = opiskelija titteli = "DI"titteli: String = DI titteli = 12345-- Error: |titteli = 12345 | ^^^^^ | Found: (12345 : Int) | Required: String
Virheilmoitusten tulkintataito kehittyy ohjelmointikokemuksen mukana. Tässä virheilmoituksessa lukee osapuilleen, että:
"Tässä yhtäsuuruusmerkin oikealta puolelta löytyi 12345, joka on kokonaisluku. Kuitenkin tällaisessa kohdassa (jossa sijoitetaan
String
-tyyppiseen muuttujaan) kaivattaisiin merkkijonoa.
Lukutyyppien yhteispeliä
On tilanteita, joissa näennäisesti rikotaan sääntöä tyyppien yhteensopivuudesta. Yksi
tällainen tilanne on Int
-arvon sijoittaminen Double
-muuttujaan, kuten seuraavan
esimerkin loppupäässä:
var lukuarvo = 123.45lukuarvo: Double = 123.45 val tasaluku = 100tasaluku: Int = 100 tasaluku * tasalukures17: Int = 10000 lukuarvo = tasalukulukuarvo: Double = 100.0 lukuarvo * tasalukures18: Double = 10000.0
Viimeinenkin sijoitus onnistui: muuttujasta tasaluku
katsottu
Int
-arvokin "kelpaa Double
-arvoksi". Mutta kuten esimerkin
viimeisistä tulosteista huomaat, Int
-arvon sijaan tulee muuttujaan
lukuarvo
tallennetuksi vastaava Double
-arvo, jolla laskeminen
tuottaa Double
-tyyppisiä tuloksia.
Tämä on hyödyllistä monessa tulevassa tilanteessa, jossa laaditaan sellainen ohjelman osa, joka toimii desimaaliluvuilla ja jonka halutaan toimivan samalla tavoin myös kokonaisluvuille.
res
-muuttujat REPLissä
REPLin val res
-alkuiset vastaukset sille syötettyihin lausekkeesiin ovat jo tulleet tutuiksi.
Itse asiassa REPL luo aina vain uusia val
-muuttujia, joiden nimet alkavat res
ja joita
voit periaatteessa käyttää itse määrittelemiesi muuttujien tapaan:
1 + 1val res19: Int = 2 res19 * 10val res20: Int = 20 val yhteensa = res19 + res20val yhteensa: Int = 22
Tämän oppikirjan REPL-esimerkeissä nämä tulosteessa toistuvat
val
-sanat eivät yleensä näy, mutta IntelliJ’ssä ne näkyvät.
val
-sanat tulostuvat kaikkien näiden rivien alkuun siksi,
että tulee määritellyksi val
-muuttujia. Kaksi ensimmäistä
REPL on nimennyt automaattisesti.
Voit hyödyntää tuota omissa kokeiluissasi, jos haluat. Tämän oppimateriaalin esimerkeissä
tätä mahdollisuutta ei kuitenkaan käytetä, vaan keskitymme opettelemaan ohjelmointitekniikoita,
jotka toimivat myös REPLin ulkopuolella. Numeroidut res
-alkuiset muuttujat ovat nimenomaan
REPL-ympäristön erikoisuus.
Yhteenvetoa
Muuttuja on nimetty tallennuspaikka yhdelle arvolle. Muuttujia käytetään tiedon varastoimiseen koneen muistissa.
Esimerkiksi GoodStuff-ohjelmassa muuttujia voidaan käyttää kirjaamaan kokemusten tietoja (hinta, arvosana) sekä se, mikä on käyttäjän suosikkikokemus.
Ohjelmoija pääsee arvoihin käsiksi muuttujien nimien avulla. Nimiä voi käyttää lausekkeina ja siis myös suurempien lausekkeiden osina.
Scalassa on kahdenlaisia muuttujia:
val
javar
.val
-muuttujaan sijoitetaan arvo, joka ei sen jälkeen vaihdu.val
-muuttujien käyttö selkeyttää ohjelmakoodia, ja niitä tulee käyttää ensisijaisesti.var
-muuttujan arvoa voi muuttaa sijoittamalla uuden arvon vanhan tilalle.var
-muuttujat mahdollistavat ohjelman tilan muuttamisen sijoituskäskyillä; niitä käytetään harkitusti tarpeen mukaan.
Järkevästi nimetyt muuttujat selkiyttävät ohjelmakoodia. Muuttujien käyttö voi parantaa myös ohjelman tehokkuutta ja muokattavuutta.
Lukuun liittyviä termejä sanastosivulla: muuttuja, sijoittaa; lauseke, arvo, evaluoida;
var
-muuttuja,val
-muuttuja; varattu sana; DRY; merkkijonoupotus.
Tässä vielä edellisen luvun käsitekaavio tämän luvun tärkeimmillä käsitteillä täydennettynä:
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.
Lisäkiitokset tähän lukuun
Luvussa tehdään vääryyttä Edvard Griegin säveltämälle musiikille. Kiitos ja anteeksi.
Käytetään avainsanaa
val
(englannin sanasta value tai value variable). Sen perään kirjoitetaan...