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

Kurssin viimeisimmän version löydät täältä: O1: 2024

Luku 1.6: Aliohjelmien käyttö

Tästä sivusta:

Pääkysymyksiä: Miten käynnistän (toisen valmiiksi tekemän) toiminnon? Miten välitän sille parametreja ja näen mitä se sai aikaan?

Mitä käsitellään? Aliohjelmia. Vaikutukselliset vs. vaikutuksettomat aliohjelmat. Aliohjelman kutsuminen: parametrit ja palautusarvot. Sisäkkäiset kutsut. Unit-arvo. Työkalujen käyttöönotto import-käskyllä.

Mitä tehdään? Ohjelmoidaan REPLissä ja luetaan.

Suuntaa antava työläysarvio:? Tunnista puoleentoista. Käytännön harjoitusten lisäksi hankalahkossa (mutta tarpeellisessa) käsitteistössä on setvimistä.

Pistearvo: A20.

Oheismoduulit: Aliohjelmia (uusi).

../_images/sound_icon.png

Muuta: Eräissä tämän luvun kohdissa on kaiuttimista tai kuulokkeista hyötyä. Aivan pakolliset ne eivät ole.

../_images/person01.png

Johdanto

Tämä ja kaksi seuraavaa lukua käsittelevät aliohjelmia (subprograms).

Aliohjelma on toteutus jollekin tietylle toiminnolle. Aliohjelma voi esimerkiksi laskea ohjelman tarvitseman laskutoimituksen tuloksen, muuttaa tietokoneen muistissa olevia tietoja jollain tarkoituksenmukaisella tavalla, tulostaa jotakin näytölle tai jonkin yhdistelmän edellisistä.

Ohjelmoija voi itse laatia uusia aliohjelmia ja käyttää niitä; hän voi myös käyttää toisten laatimia aliohjelmia. Kokonaisia ohjelmia rakennetaan aliohjelmia yhdistelemällä. Muun muassa GoodStuff-ohjelmassa on useita yhteen toimivia aliohjelmia, mutta niihin pääsemme käsiksi vasta toisella kierroksella. Tarkoitus on edetä seuraavasti:

  • Tämä luku käsittelee sitä, miten voit käyttää jo valmiiksi luotuja aliohjelmia. Tässä vaiheessa emme vielä välitä siitä, miten käyttämämme aliohjelmat on tehty.
  • Ensimmäisen kierroksen päättävissä luvuissa 1.7 ja 1.8 tutustut eräiden valmiiksi määriteltyjen aliohjelmien toteutukseen eli siihen, miten aliohjelmat on sisäisesti rakennettu ja miten ne toimivat ohjelmaa ajettaessa. Samalla pääset harjoittelemaan omienkin aliohjelmien laatimista.

Aliohjelmat ovat viimeinen niistä palasista, jotka tarvitsemme ryhtyäksemme opettelemaan laajemman ohjelmakokonaisuuden laatimista olio-ohjelmoinnin keinoin seuraavalla kierroksella.

Alkuvalmistelut

Tässä luvussa tarvitset uutta kurssimoduulia nimeltä Aliohjelmia, johon on koottu sekalaisia aliohjelmia kokeiltaviksesi. Noudata luvusta 1.2 tuttuja vaiheita (joista alla pikakertaus) noutaaksesi moduulin IntelliJ’hin ja ottaaksesi sen sisältämät aliohjelmat käyttöön REPLissä.

  1. Nouda projekti IntelliJ’hin. A+ Courses-välilehdeltä IntelliJ’n oikeasta reunasta.
  2. Käynnistä REPL Aliohjelmia-moduuliin. Valitse Aliohjelmia Project-näkymässä vasemmalla. Paina sitten Ctrl+Shift+D tai valikosta Tools → Scala REPL.

REPLin otsikkona pitäisi näkyä REPL for Aliohjelmia.

Aliohjelmaesimerkki

Ajatellaan tilannetta, jossa meillä on lukuja, vaikkapa mittaustuloksia, tallennettuna puskuriin. Esimerkiksi näin:

val tulokset = Buffer(-2, 0, 10, -100, 50, 100, 5, -5, 2)tulokset: Buffer[Int] = ArrayBuffer(-2, 0, 10, -100, 50, 100, 5, -5, 2)

Oletetaan vielä, että puskuriimme tallennetut negatiiviset luvut ovat kirjauksia epäonnistuneista mittauksista ja ne halutaan nyt poistaa puskurista.

On mahdollista määritellä aliohjelma, joka hoitaa negatiivisten lukujen poistamisen mistä tahansa puskurista. Tällainen aliohjelma onkin valmiiksi määritelty Aliohjelmia-moduuliin. Aliohjelman nimi on poistaNegatiiviset ja toiminta-ajatus seuraava:

  • Kun aliohjelmaa käytetään, sille annetaan parametriksi viittaus johonkin sellaiseen puskuriin, joka sisältää kokonaislukuja.
  • Aliohjelma muokkaa tuota puskuria poistamalla siitä kaikki negatiiviset luvut.

Aliohjelman kutsuminen ja parametrit

Kun määräämme ohjelmakoodissa tietokoneen suorittamaan jonkin aliohjelman, sanotaan, että kutsumme tuota aliohjelmaa (call, invoke, joskus myös apply). Kokeillaan nyt kutsua poistaNegatiiviset-aliohjelmaa. Se onnistuu helposti:

poistaNegatiiviset(tulokset)
Aliohjelman kutsumiskäsky koostuu aliohjelman nimestä ja sulkeiden sisäisestä parametrilausekkeesta.
Tässä tapauksessa parametrilausekkeita on vain yksi. Sen arvo on viittaus edellä luotuun puskuriin. Tämä viittaus välitetään aliohjelman käytettäväksi.

poistaNegatiiviset-kutsun suorittavalla käskyllä ei itsellään ole arvoa (tai ainakaan merkityksellistä arvoa; tästä lisää jäljempänä) samassa mielessä kuin vaikkapa lausekkeella 1 + 1. Niinpä REPL vastaa tyhjää. Kuitenkin voimme katsoa muuttujan tulokset arvon ja todeta aliohjelman muuttaneen puskuria, johon tuo muuttuja viittaa:

tuloksetres0: Buffer[Int] = ArrayBuffer(0, 10, 50, 100, 5, 2)

println ja kumppanit aliohjelmina

Oliko äskeisessä esimerkissä jotain tuttua? Juuri samaan tapaanhan olet oppinut käyttämään esimerkiksi println- ja play-käskyjä: ensin käskyn nimi ja sulkeisiin parametrilauseke.

Kyseessä ei ole sattuma. Vaikka emme tuossa vaiheessa kiinnittäneet asiaan huomiota, niin println on aliohjelma juuri samassa mielessä kuin poistaNegatiiviset. Tosin println on yleiskäyttöisempi ja määritelty osaksi Scala-kieltä, kun taas poistaNegatiiviset on kurssimateriaalin tätä lukua varten keksitty esimerkki. Aliohjelmia ovat vastaavasti myös play ja luvun 1.3 show.

Katsotaan seuraavaksi aivan toisenlaista aliohjelmaa.

Arvon palauttava aliohjelma

keskiarvo-aliohjelman idea on yksinkertainen: sille annetaan parametreiksi kaksi lukua, ja se laskee niiden keskiarvon. Tämä aliohjelma palauttaa (return) arvon:

keskiarvo(5.0, 11.0)res2: Double = 8.0
keskiarvo-aliohjelmalle kuuluu antaa kaksi parametria, joiden on oltava lukuarvoisia. Parametrilausekkeet erotetaan pilkuilla.
keskiarvo-aliohjelmakutsu on lauseke, jonka arvo on aliohjelman laskema keskiarvo. Tätä arvoa sanotaan aliohjelman palautusarvoksi (eli paluuarvoksi; return value). REPL raportoi tämänkin lausekkeen arvon tutusti.

Alla on vielä muutama lisäesimerkki tämän aliohjelman kutsumisesta. Kuten esimerkeistä näkyy, aliohjelmakutsun muodostamaa lauseketta voi sitäkin käyttää isompien lausekkeiden osana:

val eka = keskiarvo(5, 1) + 2.1eka: Double = 5.1
val toka = keskiarvo(10.9, eka) + keskiarvo(-5, -10) - 1toka: Double = -0.5
1 + keskiarvo(toka + 1, 1)res1: Double = 1.75

Aliohjelmakutsun parametrilausekkeet voivat olla literaalien lisäksi myös vaikkapa muuttujien nimiä tai muita tietotyypiltään sopivia lausekkeita.

Ohjelman tila ja siihen vaikuttaminen

Katsotaan kohta lisää esimerkkejä aliohjelmista, mutta pysähdytään ensin pohtimaan jo nähtyjen aliohjelmien piirteitä.

Olet nähnyt tässä luvussa kaksi hyvin erilaista aliohjelmaa:

  1. poistaNegatiiviset-aliohjelman kutsut käskevät tietokonetta hoitamaan tietyn asian. Tällainen kutsu aiheuttaa muutoksia tilaan (state), jonka ohjelma on tallentanut tietokoneen muistiin. Voimme sanoa tällaista aliohjelmaa vaikutukselliseksi (effectful) aliohjelmaksi. Vaikutuksellinen aliohjelma ei välttämättä palauta mitään kiinnostavaa arvoa.
  2. keskiarvo-kutsu ei muokkaa mitään muistiin tallennettua tietoa. Voimme sanoa tällaista aliohjelmaa vaikutuksettomaksi (effect-free) aliohjelmaksi. Jotta se olisi hyödyllinen, vaikutuksettoman aliohjelman on tuotettava merkityksellinen palautusarvo, minkä keskiarvo tekeekin.

Lisäksi olemme käyttäneet println-aliohjelmaa, joka tulostaa merkkejä näytölle. Kun println-käsky suoritetaan, se vaikuttaa maailman tilaan siinä mielessä, että ohjelman näytölle tuottama tuloste muuttuu havaittavasti. Myös println kuuluu siis vaikutuksellisten aliohjelmien kategoriaan kuten poistaNegatiivisetkin. Luvun 1.3 esittelemät play ja show ovat nekin vastaavasti vaikutuksellisia.

Saman luvun circle ja rectangle ovat myös aliohjelmia. Ne ovat vaikutuksettomia samassa mielessä kuin keskiarvo. Siinä missä keskiarvo vain laskee ja palauttaa parametriensa perusteella lukuarvon, circle ja rectangle vain tuottavat parametriensa perusteella kuvan.

Kustakin aliohjelmasta voi pohtia: Muuttuuko jokin asia aliohjelman suorituksen seurauksena havaittavalla tavalla? (Ajan kulumista suorituksen aikana ja palautusarvon saamista ei lasketa muuttumiseksi.) Aliohjelmat voidaan jakaa vaikutuksellisiin ja vaikutuksettomiin tällä perusteella. Tässä vaiheessa kurssia tämä jako jo osoittaa meille, että aliohjelmilla voi tehdä erilaisia asioita. Myöhemmin osoittautuu, että jaottelulla on laaja-alainen merkitys ohjelmoinnin kannalta. On esimerkiksi olemassa ohjelmointisuuntauksia, joissa käytetään vain vaikutuksettomia aliohjelmia (luku 10.2). Ei mennä vielä siihen; voimme sen sijaan jo nyt hyödyntää tätä jaottelua selkiyttämään aliohjelmiin liittyviä termejä ja käsitteitä.

Tärkeä termi: funktio

Useiden ohjelmointikielten yhteydessä kaikkia aliohjelmia on tapana sanoa funktioiksi (function), niin vaikutuksellisia kuin vaikutuksettomiakin. Näin on esimerkiksi Scala-kielen tapauksessa. Jatkossa puhummekin usein funktioista, ja tällä kurssilla funktio on siis sama asia kuin aliohjelma.

"Funktio" kuulostaa sanana tutulta, ja syystäkin, mutta:

Varo matikkaa! (jälleen kerran)

Moni ohjelmoinnin aloittelija on kompastellut funktion käsitteeseen tuon termin matematiikkayhteyden takia.

On totta, että vaikutuksettomat funktiot muistuttavat koulumatematiikan funktioita: otetaan sisään parametriarvoja ja tuotetaan jokin tulos (palautusarvo), ja siinä kaikki. Kun vain selvitetään, mikä on matemaattisen funktion f(x) arvo tietylle x:lle, ei muutu mitään tallennettua tietoa tai tulostu mitään näytölle.

Kuitenkin kun Scala-funktiota kutsutaan, tulostuksia tai muita vaikutuksia ohjelman tilaan voi tapahtua. On tärkeää huomata, että esimerkiksi Scala-ohjelmoinnissa funktion määritelmä kattaa myös vaikutukselliset aliohjelmat. Pidä tämä mielessä, kun jatkossa puhutaan funktioista!

Myös vaikutuksettomilla funktioilla on tietokoneohjelmoinnissa oleellinen ero tutunlaisiin matemaattisiin funktioihin verrattuna: ne eivät ainoastaan kuvaa riippuvuussuhteita kuten f(x) = x + 1, vaan myös niitä vaiheittaisia prosesseja (algoritmeja), joilla palautusarvot saadaan laskettua. Tämä seikka konkretisoituu seuraavassa luvussa.

Erilaisista funktioista kootusti

Tässä taulukko mainituista funktiotyypeistä. Eräät kohdista on alleviivattu; viemällä hiiren kursorin niiden päälle saat lisäselityksiä niistä.

Vaikuttaako funktio tilaan? Palauttaako arvon? Kurssilla käytetty termi Muita termejä
Ei vaikuta koskaan Palauttaa Vaikutukseton funktio Sivuvaikutukseton funktio
Ei palauta Tyhjä funktio  
Vaikuttaa ainakin joskus Palauttaa
Vaikutuksellinen
funktio
Sivuvaikutuksellinen funktio, proseduuri
Ei palauta

Kirjastofunktioita pakkauksesta scala.math

Scala-kielen mukana tulee pakkaus nimeltä scala.math, joka sisältää funktioita yleisiin matemaattisiin tarpeisiin.

Esimerkiksi funktio sqrt laskee ja palauttaa neliöjuuren (square root):

scala.math.sqrt(100)res2: Double = 10.0
scala.math.sqrt(25)res3: Double = 5.0
Kutsumme tässä funktiota sen täydellisellä nimellä, jossa yhdistyvät pakkauksen nimi scala.math ja funktion varsinainen nimi sqrt. (Alempana kerrotaan, miten tämä käy kätevämmin.)
Tämä vaikutukseton funktio ei muuta tilaa. Se vain palauttaa tuloksen.

Saman pakkauksen funktiot max ja min ovat monissa ohjelmissa käteviä. Nämä vaikutuksettomat funktiot palauttavat kahdesta parametristaan suuremman ja pienemmän:

scala.math.max(2, 10)res4: Int = 10
scala.math.max(15, -20)res5: Int = 15
scala.math.min(2, 10)res6: Int = 2
scala.math.min(15, -20)res7: Int = -20

Luvussa 1.1 jo mainittiinkin, että ohjelman rakennuspalikoita, jotka ovat tarjolla toisten ohjelmoijien käyttöä varten, sanotaan usein kirjastoksi (library). Kirjaston sisältämiä funktioita — kuten äskeisiä — sanotaan vastaavasti kirjastofunktioiksi.

Saimme nyt kutsuttua noita kirjastofunktioita, mutta oli melko kurjaa kirjoittaa tuo pakkauksen nimi scala.math jokaiseen funktiokutsuun. Tähän monisanaisuuteen kuitenkin löytyy lääke. Perehdytään siihen tässä välissä ja tutkitaan sitten lisää funktioita.

Pakkauksista ja import-käskyistä

Koodi on tarjolla pakkauksissa

Kurssilla on jo mainittu muutamia koodipakkauksia. Esimerkiksi:

  • Äsken käytimme funktiota pakkauksesta scala.math. (Tuo pakkaus tulee Scala-työkaluston mukana.)
  • Kurssityökalut kuten play, Color ja Pic sisältää pakkaus nimeltä o1. (Sen koodi löytyy IntelliJ’ssä O1Library-moduulista.)
  • Luvussa 1.5 mainittiin Buffer-tyypin määrittelevä pakkaus scala.collection.mutable. (Tämäkin pakkaus tulee Scala-työkalustossa.)

Työkalujen jaottelu eri pakkauksiin on tarpeen muun muassa siksi, että isompiin ohjelmiin voi helpostikin tulla keskenään samannimisiä osia esimerkiksi eri ohjelmoijien laatimina. Nimet kuten play, show tai min voivat olla toisissa ohjelmissa muunmerkityksisiä kuin esimerkeissämme.

Ongelma ratkeaa sijoittamalla keskenään samannimiset osat eri pakkauksiin, jolloin pakkauksen nimellä voi ilmaista, minkä pakkauksen sisällöstä on kyse. Haittapuolena on, että kun tarvitsemme työkaluja jostakin pakkauksesta, on koneelle tavalla tai toisella ohjeistettava, että käytämme juuri tuon pakkauksen sisältöä. Pakkauksen nimen toistuva kirjoittaminen on ärsyttävää ja voi vaikeuttaa koodin lukemista.

Kätevämpää pakkausten käyttöä: import-käsky

Voimme ilmoittaa etukäteen, että aiomme myöhemmässä koodissa käyttää tietyn pakkauksen sisältöä:

import scala.math.sqrtimport scala.math.sqrt

Käytännössä tuo import-käsky tarkoittaa: "Aina, kun jäljempänä sanotaan sqrt, niin se tarkoittaa juuri scala.math-pakkauksen määrittelemää sqrttä."

import-käsky ei ole lauseke eikä sillä ole varsinaista arvoa. REPL kuittaa käskyn toistamalla sen.

Näin ilmoitettuamme voimme kutsua tuota funktiota lyhyttä nimeä sqrt käyttäen:

sqrt(100)res8: Double = 10.0
sqrt(25)res9: Double = 5.0

Ellei import-käskyä olisi ensin annettu, olisimme saaneet tällaisen virheilmoituksen:

sqrt(100) ^
 error: not found: value sqrt

Jos saat ohjelmoidessasi tuollaisen ilmoituksen, tarkista ensin oikeinkirjoitus ja toiseksi, että olet muistanut antaa import-käskyn.

Koko pakkaus käyttöön alaviivalla

Usein halutaan käyttää samasta pakkauksesta monia eri työkaluja; saatamme vaikkapa haluta käyttää scala.math-pakkauksesta useaa eri funktiota.

Yksi mahdollisuus on importata kukin funktio erikseen, mutta usein kätevämpää on ottaa pakkauksen koko sisältö käyttöön kerralla:

import scala.math._import scala.math._
Alaviivalla _ on Scalassa useita käyttöyhteydestä riippuvia merkityksiä, mutta yleisesti ottaen se tarkoittaa suunnilleen "mikä vain" tai "kaikki".

Nyt voimme käyttää mitä vain tuon pakkauksen funktioista lyhyin merkinnöin:

sqrt(100)res10: Double = 10.0
max(2, 10)res11: Int = 10
min(2, 10)res12: Int = 2

Kysyttyä: kuluuko enemmän muistia, jos käytän alaviivaa import-käskyssä?

Ei. importilla ainoastaan kirjataan, mitä tarkoitetaan, kun käytetään tiettyjä nimiä kyseisessä ohjelmakoodissa. Esimerkiksi import scala.math._ määrittää, että kun käytetään mitä tahansa nimistä min, max, sqrt jne., tarkoitetaan juuri scala.math-pakkauksen osia.

Tietokoneen täytyy kyllä pitää muistissa ohjelman käyttämät työkalut (funktiot yms.). Kuitenkin ohjelman ajonaikaisen muistinkäytön kannalta oleellista on nimenomaan se, mitä työkaluja todella käytetään, eikä se, millaisella ilmaisulla niitä on ilmoitettu käytettävän.

Milloin import-käskyä ei tarvita?

Tarvitsimme siis import-käskyä käyttääksemme scala.math-pakkausta kätevästi. Myöhemmin käytämme sitä myös muihin pakkauksiin. Kuitenkin tähän saakka pärjäsimme hyvin ilmankin. Olemme käyttäneet esimerkiksi funktioita println, play ja show sekä tyyppejä Int, String ja Buffer ilman import-käskyä; ne ovat "toimineet suoraan". Kuitenkin nekin sijaitsevat eräissä pakkauksissa. Miksei niitä tarvinnut importata?

Ensinnäkin: import-käskyä ei tarvitse antaa, jos kyseessä on Scala-kieleen aivan erottamattomasti liittyvä pakkaus, jonka nimi on yksinkertaisesti scala. Tämä erikoispakkaus määrittelee mm. tyypit Int, Double ja String; esimerkiksi Int-tietotyypin täydellinen nimi on scala.Int. Tuon pakkauksen sisältö on aina käytettävissä kaikissa Scala-ohjelmissa.

Toiseksi: käyttämämme REPL-ympäristö valitsee käyttöösi eräitä pakkauksia automaattisesti, jotta et joudu naputtelemaan sinne joka session alussa samoina toistuvia import-käskyjä. Tarkemmin sanoen: kun käynnistät REPLin johonkin kurssimoduuliin, se importaa automaattisesti seuraavasti.

  • Saat käyttöösi pakkauksen o1 aivan kuin olisit itse aluksi kirjoittanut import o1._. Tämän vuoksi esimerkiksi play ja Pic toimivat REPLissä helposti. Joissakin moduuleissa REPL importaa automaattisesti myös muita moduulin sisältämiä pakkauksia (mikä näkyy silloin REPLin tervehdystulosteessa). Esimerkiksi kun käynnistit REPLin Aliohjelmia-moduuliin, REPL poimi käyttöön pakkausten o1 ja o1.aliohjelmia sisällön.
  • Saat käyttöösi Buffer-kokoelmat pakkauksesta scala.collection.mutable aivan kuin olisit itse aluksi importannut ne. Käytämme puskureita kurssin alun REPL-esimerkeissä usein, joten halusimme säästää sormiasi hieman.

Jos kuitenkin annat turhan import-käskyn, niin ei siitä mitään kauheaa tapahdu. Jos taas unohdat tarvittavan importin, saat not found -virheilmoituksen yllä kuvattuun tapaan ja voit korjata asian.

Kysyttyä: import ja nimikonfliktit

Mitä tapahtuu jos importtaa kaksi pakkausta, joissa on kaksi samannimistä funktiota, jonka jälkeen käyttää sitä funktiota ilman pakkauksen nimeä (kumman Scala valitsee, vai valitseeko kumpaakaan)?

Moniselitteisestä nimestä tulee virheilmoitus. Tuollaiset nimikonfliktit eivät ole mikään harvinaisuus, varsinkaan kun yhdistelee eri ohjelmoijien laatimia kirjastoja.

Osan nimikonflikteista voi kiertää välttämällä alaviivaa import-käskyissä ja valitsemalla käyttöön vain ne osat kustakin pakkauksesta, joita todella tarvitset.

Jos kuitenkin tarvitset samassa ohjelmassa kahta samannimistä funktiota eri pakkauksista, voit aina käyttää funktioiden täydellistä nimeä, jossa on mukana pakkauksen nimi pisteineen alussa. Toinen vaihtoehto on ottaa ainakin toinen funktioista käyttöön toisella nimellä. Se käy tähän tapaan:

import scala.math.{abs => itseisarvo}import scala.math.{abs=>itseisarvo}
itseisarvo(-100)res13: Int = 100

Sitten lisää funktioita.

Funktiokavalkadi

Seuraavaksi tutustumme esimerkin vuoksi koko joukkoon eri funktioita. Näin saat käsitystä siitä, mitä funktiot voivat tehdä, ja pääset harjoittelemaan funktiokutsuja sisältävän ohjelmakoodin lukemista.

Käytämme sekä eräitä Scalan kirjastofunktioita, jotka ovat hyvin yleiskäyttöisiä, että eräitä kurssia varten laadittuja esimerkkifunktioita.

Lisää matemaattisia kirjastofunktioita

Yllä esiteltiin scala.math-pakkauksen funktiot sqrt, max ja min. Samassa pakkauksessa on muitakin funktioita, joilla voi määrätä suoritettavaksi erilaisia laskutoimituksia.

Alla on esimerkkejä funktioista abs (absolute value: itseisarvo), pow (power: potenssiin korotus) ja sin (sine: sinifunktio). Nämä funktiot on otettava käyttöön joko yksittäin tai kaikki kerralla kuten tässä:

import scala.math._import scala.math._
abs(-50)res14: Int = 50
pow(10, 3)res15: Double = 1000.0
sin(1)res16: Double = 0.8414709848078965

Kaikki nämä funktiot ovat vaikutuksettomia. Ne vain palauttavat arvoja eivätkä muuta tallennettua dataa tai tulosta tai soita tai piirrä mitään.

Kokeile ainakin joitakin esitellyistä funktioista itse REPLissä. Voit kokeilla myös vaikkapa seuraavia: muut trigonometriset funktiot (cos, atan jne.), cbrt (kuutiojuuri), hypot (hypotenuusa; parametreiksi kaksi kateetinmittaa), floor (alaspäin pyöristys), ceil (ylöspäin pyöristys), round (lähimpään pyöristys), log ja log10 (logaritmeja). Koko luettelo löytyy Scalan dokumentaatiosta, joka tosin ei ole kaikilta osin aloittelijaystävällinen.

Tarkoituksena ei nyt missään nimessä ole opetella mainittuja kirjastofunktioiden nimiä ulkoa, vaikka ne voivatkin myöhemmin tulla tarpeeseen. Nimet voi tarvitessa tarkistaa dokumentaatiosta tai täältä oppimateriaalista. Jos ja kun jokin funktio on niin yleishyödyllinen, että sitä tulee tarvittua usein, niin nimen oppii ulkoa vahingossakin. (Vinkkinä kuitenkin, että erityisesti max- ja min-funktioita käytetään tulevissa luvuissa runsaasti.)

Funktionkäyttöesimerkkejä ja -tehtäviä

Funktio voi hyödyntää ulkoisia resursseja kuten tiedostoja tai nettilähteitä. Seuraava kurssia varten laadittu esimerkkifunktio selvittää, mikä on IMDb-leffasivustolla äänestettyjen Top 250 -elokuvien listalla tietyllä sijaluvulla:

imdbLeffa(3)res17: String = The Godfather: Part II
imdbLeffa(1)res18: String = The Shawshank Redemption

Esimerkkifunktio toimii ilman nettiyhteyttäkin, koska se on laadittu ottamaan tietonsa Aliohjelmia-moduulin mukana tulevasta kansiosta top_movies. (Leffaluettelo ei siis ole aivan ajantasainen.)

Käytä funktiota imdbLeffa selvittääksesi, mikä on IMDb:n listan mukaan kaikkien aikojen 150. paras elokuva. Numerointi alkaa tässä luonnollisella tavalla ykkösestä, eli äskeisen esimerkin elokuvat ovat listan kolmas ja ensimmäinen.

Kirjoita 150. leffan nimi tähän:

Funktio imdbParhaatOhjaajat muodostaa IMDb:n leffalistan perusteella luettelon, jossa ovat ohjaajat sen mukaan järjestettynä, montako elokuvaa heillä on listalla. Funktio palauttaa tällaisen luettelon merkkijonona. Funktiolle annetaan yksi kokonaislukuparametri, joka on alaraja luetteloon mukaan ottamiselle; esimerkiksi parametriarvolla 2 luetellaan vain ne ohjaajat, joilla on vähintään kaksi elokuvaa Top 250:ssä.

Käytä funktiota imdbParhaatOhjaajat selvittääksesi, kenellä on kaikkein eniten elokuvia listalla. Kirjoita eniten menestyneitä leffoja ohjanneen nimi tähän. (Heitä on kaksi tasoissa. Kumman tahansa nimi kelpaa.)

Käytä funktiota imdbAikavalinParas selvittääksesi, mikä on IMDb:n mukaan koko 1950-luvun paras elokuva. Funktiolle annetaan kaksi kokonaislukuparametria: alkuvuosi ja loppuvuosi. Se palauttaa tämän aikavälin korkeimmalle sijoittuvan elokuvan nimen. Alkuvuosi ja loppuvuosi sisältyvät annettuun aikaväliin.

Kirjoita 50-luvun parhaaksi arvioidun leffan nimi tähän:

Käytetään nyt merkkijonoja parametreina eräälle valmiiksi määritellylle funktiolle. Funktion nimi on editointietaisyys, ja tarkemmin sanoen se laskee kahden parametrimerkkijononsa välisen Levenšteinin etäisyyden.

../_images/levenshtein-fi.png

Levenšteinin etäisyys on kokonaisluku, joka kertoo, montako yhden merkin lisäystä, poistoa tai toiseksi merkiksi vaihtamista vähintään tarvitaan muuntamaan tietty merkkijono tietyksi toiseksi merkkijonoksi. Esimerkiksi merkkijonojen "scala" ja "osata" Levenšteinin etäisyys on kolme, koska tämän vähemmillä muutoksilla ei pääse:

  1. Lisätään merkkijonoon "scala" yksi o-kirjain niin saadaan "oscala".
  2. Poistetaan c-kirjain niin saadaan "osala".
  3. Vaihdetaan yksi kirjain niin saadaan "osata".

(Levenšteinin etäisyyttä hyödynnetään paitsi oikoluvussa myös muun muassa geenitutkimuksessa, kun vertaillaan DNA-sekvenssejä. Eliön perimä voidaan kuvata merkkijonona, jossa DNA:n eri osia vastaavat esimerkiksi kirjaimet A, G, C ja T.)

Kutsu o1-pakkauksen sisältämää editointietaisyys-funktiota. Sille pitää antaa kaksi String-tyyppistä parametria eli vertailtavat merkkijonot. Muista lainausmerkit merkkijonoliteraalien ympärille ja pilkku.

Voit kokeilla funktion toimintaa eri parametriarvoilla. Raportoi alle merkkijonojen "päivänsäde" ja "menninkäinen" Levenšteinin etäisyys.

Tämä tehtävä on täysin vapaaehtoinen, kuten kaikki muutkin harmaiden laatikoiden lisätreeni- ja haastetehtävät, joita esiintyy tulevissa luvuissa siellä täällä. A+ antaa nimellisesti yhden pisteen kustakin, mutta se ei ole A-, B- tai C-piste eikä vaikuta arvosanaan.

Tehdään animaatio.

Määrittele muuttujia seuraavasti.

val lampunKoko = 250
val punainen  = circle(lampunKoko, Red)
val keltainen = circle(lampunKoko, Yellow)
val vihrea    = circle(lampunKoko, Green)
val liikennevalot = Buffer(punainen, keltainen, vihrea)

o1-pakkauksessa on vaikutuksellinen funktio animoi, joka esittää kuvia peräkkäin ja muodostaa näin niistä animaation. Funktiolle annetaan kaksi parametria:

  • puskurillinen kuvia, ja
  • positiivinen luku (Double), joka kertoo kuinka nopeasti animaatio etenee. Isompi lukuarvo tuottaa nopeamman animaation.

Kokeile animoi-funktiota. Kirjoita alle sellainen käsky, joka animoi liikennevalot-muuttujan osoittaman puskurin sisällön nopeudella 1.0. Voit itse keksiä muutakin animoitavaa.

Toimivatko "ääkköset" REPLissäsi?

Varmistapa ennen seuraavaa tehtävää, toimivatko suomen "ääkköset" REPLissäsi kunnolla. Joissakin ympäristöissä näin ei nimittäin ole.

Eli näyttääkö tältä, kuten pitäisi, vai tulostuuko ä- ja ö-kirjainten tilalle kysymysmerkkejä tai muuta "sotkua"?

"äääööö"res19: String = äääööö

Jos ääkköset näkyvät noin, jatka eteenpäin. Jos eivät, suosittelemme että teet pienen säädön IntelliJ’n asetuksiin:

  1. Valitse ylävalikosta Help → Edit Custom VM Options. Editoriin avautuu tiedosto, jossa on erinäisiä asetustietoja (joita ei nyt tarvitse ymmärtää).
  2. Lisää perään tällainen rivi: -Dfile.encoding=UTF-8
    • Huomaa, että rivi alkaa viivalla.
  3. Sulje IntelliJ ja käynnistä uudestaan. Ääkkösten pitäisi nyt näkyä kunnolla.
    • (Aiempi import scala.math._ ei ole voimassa uudessa REPL-sessiossa, joten anna se tässä välissä uudestaan tulevia tehtäviä varten.)

Yksinkertainen tapa soittaa Jaakko kulta on tässä. Kokeile.

val jaakkoKulta = "f-g-a-f-f-g-a-f-a-hb->c---<a-hb->c---cdc<hba-f->cdc<hba-f-f-c-f---f-c-f---"
play(jaakkoKulta)

Jaakko kulta esitetään usein moniäänisesti kaanonissa: kukin ääni aloittaa melodian hieman eri aikaan, ja äänet menevät osin päällekkäin. Kaanon-esitystä kuvaavan merkkijonon voi muodostaa vaikutuksettomalla funktiolla kaanon, joka on määritelty seuraavasti.

  • Sille annetaan ensimmäiseksi parametriksi merkkijono, joka sisältää jonkin melodian.
  • Sille annetaan toiseksi parametriksi viittaus kokonaislukuja sisältävään puskuriin, jossa on lueteltuna soittimien numeroita.
  • Kolmanneksi ja viimeiseksi parametriksi annetaan kokonaisluku, joka määrää viiveen, jonka kukin ääni "odottaa" aloittamistaan edellisen äänen aloitettua.
  • Funktio palauttaa merkkijonon, joka kuvaa moniäänisen kaanonversion annetusta melodiasta siten, että äänet soitetaan toisen parametrin luettelemilla soittimilla. (Tällaisen merkkijonon voi sitten antaa play-funktiolle soitettavaksi.)

Tässä melkein valmis esimerkkikoodi, jolla yllä kuvailtua kaanon-funktiota voi käyttää:

val jaakkoKulta = "f-g-a-f-f-g-a-f-a-hb->c---<a-hb->c---cdc<hba-f->cdc<hba-f-f-c-f---f-c-f---"
val soittimet = Buffer(4, 1, 74, 19)
play(???)

Millainen kaanon-funktion kutsu pitäisi kirjoittaa play-funktion parametrilausekkeeksi kolmen kysymysmerkin paikalle, jotta kaanonissa soisi tuo melodia noilla soittimilla ja viiveellä 8?

Kokeile REPLissä ja kirjoita pyydetty lauseke myös tähän alle. (Vain se kysymysmerkit korvaava lauseke, ei koko soittokäskyä eikä sitä pitkää merkkijonoa, jonka kaanon-funktio palauttaa.) Käytä yllä määriteltyjä muuttujia.

Voit kokeilla kaanon-funktion kutsumista muutenkin.

Harjoittele vielä funktion kutsumista ja puskuriin osoittavan viittauksen käyttämistä parametrina.

Kokeile o1-pakkauksen funktiota sensuroi. Anna sille kaksi parametria:

  1. sensuroitava merkkijono "Sylvi sentään! Voi herttinen! Mikä tavaton se oli? Oi, hyvä Sylvi!"
  2. viittaus puskuriin (Buffer), jossa on kolme kirosanamerkkijonoa "herttinen", "Sylvi" ja "tavaton".

Minkä tekstin tämä funktiokutsu palauttaa?

Voit ratkaista tehtävän esimerkiksi näin: Luo ensin puskuriin viittaava muuttuja. Välitä sitten sensuroi-funktiolle parametreiksi sensuroitava merkkijono literaalina sekä viittaus luomasi muuttujan osoittamaan puskuriin.

Edellisen kysymyksen koodista voi olla apua. Muutkin ratkaisutavat ovat mahdollisia ja sallittuja.

Kirjoita merkkijonot täsmälleen yllä olevassa muodossa. Voit kopioida vastauksen tulosteesta, niin menee merkilleen oikein.

Luvussa 1.4 esiteltiin tapa upottaa merkkijonon sisään arvoja käyttäen s-kirjainta ja dollarimerkkiä. Koetetaan nyt tuota tekniikkaa ja funktiokutsuja yhdessä.

Seuraavien kolmen koodinpätkän pitäisi kunkin tulostaa luku ja sen neliöjuuri. Mitkä niistä toimivat? Arvioi itse ja kokeile REPLissä.

(Alla oletetaan, että import scala.math._ on annettu.)

val luku = 123.4
val juuri = sqrt(luku)
println(s"Luvun $luku neliöjuuri on $juuri.")
val luku = 123.4
println(s"Luvun $luku neliöjuuri on ${sqrt(luku)}.")
val luku = 123.4
println(s"Luvun $luku neliöjuuri on $sqrt(luku).")

Mikä seuraavista pitää paikkansa?

../_images/pylpyraattori-fi.png

Vielä yksi esimerkki

On myös mahdollista laatia funktio, joka vuorovaikuttaa ohjelman käyttäjän kanssa, kun sitä kutsutaan. Tästä esimerkkinä on laadittu käyttöösi funktio nimeltä pelaaPylpyrapelia. Se haastaa sinut yksinkertaiseen peliin, jossa kone pääsee pätemään. Voit kokeilla sitä omin päin. Funktiolle tulee antaa parametriksi pelaajan nimi merkkijonona.

Funktiokutsuja sisäkkäin

Funktiokutsuun merkitään parametreiksi sopivantyyppisiä lausekkeita. Funktiokutsu itsekin on lauseke. Niinpä funktiokutsuja voi laittaa sisäkkäin:

Jos sisäkkäisiä funktiokutsuja on paljon, voi olla selkeämpää muotoilla ohjelmakoodi toisin esimerkiksi muuttujien avulla. Animaatioesimerkin viimeisen rivin voisi korvata vaikkapa näillä käskyillä:

val potenssi = pow(2, 5)potenssi: Double = 32.0
val pienempi = min(potenssi, 100 - sqrt(100))pienempi: Double = 32.0
println(abs(-5.5) + pienempi)37.5

Tai vaikka näinkin:

val potenssi = pow(2, 5)potenssi: Double = 32.0
val erotus = 100 - sqrt(100)erotus: Double = 90.0
val pienempi = min(potenssi, erotus)pienempi: Double = 32.0
val lopputulos = abs(-5.5) + pienempilopputulos: Double = 37.5
println(lopputulos)37.5

Käytä tässä tyyliasiassa maalaisjärkeä. Mieti tapauskohtaisesti, mikä on itsellesi selkein ja luontevin tapa. Kaikkiin esitettyihin tyyleihin on joka tapauksessa syytä totutella, vaikka niitä et kaikkia itse käyttäisikään, sillä ohjelmointiin (ja ohjelmoinnin opiskeluun) sisältyy myös paljon toisten kirjoittaman koodin lukemista.

Pikkutehtävä: lausekkeen evaluointijärjestys

Tarkastele seuraavaa lauseketta:

max(4, 5) * min(6 - pow(5, sqrt(2) + 1), 120 / abs(-10))

Kun tämä lauseke evaluoidaan, mitkä kaikki lausekkeessa esiintyvistä toimenpiteistä suoritetaan ennen itseisarvon (abs) laskemista?

Unit-arvo

Scalassa (ja useassa muussa kielessä) on erityislaatuinen arvo nimeltä Unit. Tätä arvoa käytetään sellaisten funktioiden palautusarvona, jotka eivät palauta mitään merkityksellistä arvoa. Tällaisia funktioitahan ovat esimerkiksi println, play ja yllä kuvailtu poistaNegatiiviset. Teknisesti ottaen nämäkin funktiot palauttavat nimittäin arvon — Unit-arvon.

Unit-palautusarvolla ei voi tehdä käytännöllisesti katsoen mitään. Voit ajatella, että se vain tarkoittaa: "funktion suoritus päättyi, mutta mitään oleellista ei syntynyt palautusarvona". Esimerkiksi poistaNegatiiviset-funktio vain muuttaa puskurin tilaa eikä tuota mitään "tulosta". Käytännössä voimme tällä kurssilla ajatella ja sanoakin, että Unit tarkoittaa "ei mitään palautusarvoa" ja että esimerkiksi println- ja poistaNegatiiviset-funktiot "eivät palauta arvoa".

Vaikka se ei Scala-ohjelmakoodissa välttämättä kovin usein näykään, Unit-arvo ei ole aloittelijallekaan pelkkä kuriositeetti. Sen olemassaolosta on hyvä tietää jo vaikka siksikin, että sana esiintyy joskus virheilmoituksissa. Esimerkiksi yritys laskea 1 + println("kissa") tuottaa virheilmoituksen, jossa valitetaan sitä, ettei ykköstä voi laskea Unitin kanssa yhteen. Lisäksi Unit-sanaa käytetään runsaasti Scala-ohjelmien kuvauksissa (dokumentaatiossa; luku 3.2), kun halutaan ilmoittaa, ettei tietyllä ohjelman osalla ole merkityksellistä palautusarvoa.

Unit kurssimateriaalin animaatioissa

Aiemmissa kurssimateriaaliin upotetuissa animaatioissa Unit-palautusarvoja ei ole näkynyt. Tulevissa ne kuitenkin näkyvät kuten seuraavassa pikkuanimaatiossa.

println-funktiokutsukin palauttaa arvon, joskin sisällöttömän sellaisen:

Palauttaminen vs. tulostaminen

Aloitteleville ohjelmoijille tuottaa joskus vaikeuksia hahmottaa arvon tulostamisen ja arvon palauttamisen välistä eroa. REPLin käyttö ei välttämättä selkiytä tilannetta. Korostetaan siis vielä tätä eroa:

  • Palauttaminen on yleinen funktioiden piirre. Funktio voi palauttaa arvon "vastauksena" sille taholle, joka funktiota kutsuu.
    • Palautusarvo ei välttämättä tule mihinkään näkyviin.
    • Funktiota kutsunut ohjelmakoodi voi tehdä arvolla mitä vain, esimerkiksi käyttää palautusarvoa laskutoimituksen osana, tulostaa sen, välittää sen play-funktiolle tai vain jättää sen käyttämättä.
    • Pelkkä palauttaminen ei vaikuta ohjelman tilaan edellä kuvaillussa mielessä.
  • Tulostaminen tarkoittaa tekstin laittamista näkyviin esimerkiksi tietokoneen näytölle. Tämä onnistuu Scalassa println-käskyllä.
    • Tulostettavan tekstin voi määrätä minkä vain lausekkeen arvo, esim. println("Moi") tai println(1 + 1).
    • Lausekkeena voi olla myös funktiokutsu, vaikkapa println(keskiarvo(10, 20)), jolloin tulostetaan funktiota kutsumalla saatu palautusarvo.
    • Tulostamisen luemme ohjelman tilaan vaikuttamiseksi.

Palauttaminen ja tulostaminen REPLissä

REPL evaluoi sinne kirjoitetut lausekkeet ja tulostaa automaattisesti kuvaukset niiden arvoista. Funktiokutsulausekkeen tapauksessa tämä tarkoittaa, että suoritetaan funktio ja tulostetaan kuvaus sen palauttamasta arvosta. Esimerkiksi kun REPLiin syötetään lauseke max(3 + min(1, 4), 3), niin tulostuu kuvaus max-funktion palautusarvosta. Kuitenkaan esimerkiksi min-funktion palautusarvoa 1, jota käytettiin koko lausekkeen arvon määrittämiseen, ei tulosteta.

Kun REPLissä kutsutaan funktiota, joka palauttaa Unit-arvon, niin REPL ei tulosta tätä sisällötöntä palautusarvoa.

Palauttaminen ja tulostaminen samassa funktiossa

Edellä jo näkyi, että println paitsi tulostaa myös palauttaa arvon, joskin vain Unit-arvon. Voidaan toki määritellä sellainenkin funktio, joka sekä tulostaa näkyviin jotain että tuottaa varsinaisen palautusarvon.

Funktioista ja abstraktioista

Funktion laatiminen työkaluksi

Jos tavoitteenamme on saada laskettua vaikkapa jonkin luvun sini tai neliöjuuri, niin valmis työkalu löytyy kirjastofunktiona. Kun valmista ratkaisua ei löydy, muodostamme oman ratkaisun olemassa olevia työkaluja yhdistelemällä. Jos esimerkiksi haluamme saada selville, paljonko on sellaisen pallon tilavuus, jonka säde on 6371 km, niin voimme hyödyntää aritmeettisia operaattoreita ja pow-potenssiinkorotusfunktiota:

import scala.math.powimport scala.math.pow
4 * 3.14159 * pow(6371, 3) / 3res20: Double = 1.0832060019000126E12

Tuossa vain evaluoimme tiettyjä arvoja sisältävän lausekkeen, mistä saimme yhden halutun arvon tulokseksi. Jos kuitenkin haluamme pystyä käsittelemään erilaisia tapauksia — kuten laskemaan erikokoisten pallojen tilavuuksia — on kätevämpää rakentaa oma työkalu juuri tähän tarkoitukseen. Se voisi toimia vaikkapa näin:

pallonTilavuus(6371)res21: Double = 1.0832060019000126E12
pallonTilavuus(1)res22: Double = 4.188786666666666

Funktioiden hyötyjä

Yksi käytännön syy funktioiden määrittelemiselle tuli juuri esille. Kun tietty kenties monimutkainenkin toiminto on kerran toteutettu funktioksi, voi toimintoa käyttää helposti funktiota kutsumalla. Pallon uudelleen keksimiseltä vältytään siinäkin mielessä, että toistuvasti tarvittu toiminto voidaan toteuttaa funktioksi monen ohjelmoijan käyttöön.

Lisäksi funktiot auttavat jaottelemaan ohjelmakoodin selkeärajaisiin, nimettyihin osakokonaisuuksiin. Tämä parantaa ohjelmakoodin luettavuutta ja muokattavuutta.

Kolmannenlainen funktioiden tuoma hyöty on, että funktio piilottaa sisälleen jonkin toiminnon toteutuksen, jonka yksityiskohtia funktion käyttäjän ei tarvitse tuntea. Jos pallonTilavuus-funktio on määritelty, sitä voi käyttää sellainenkin, joka ei tunne kyseistä laskentakaavaa. Olet itse tässä luvussa käyttänyt erilaisia sisäisesti melko monimutkaisiakin funktioita, vaikka et opetetun perusteella osaisi niitä itse toteuttaa tai niiden toteutusta ymmärtää. On riittänyt, että tunnet tietyt piirteet funktiosta: mitä parametreja se ottaa, mitä vaikutuksia (jos mitään) se aiheuttaa ja mitä se palauttaa.

Funktiot ovat siis eräs abstrahoinnin (abstraction) muoto.

Abstraktiot ohjelmoinnissa

abstract: something that concentrates in itself the essential qualities
of anything more extensive or more general; essence

—yksi sanan "abstract" määritelmistä Infoplease.com-sivustolla

Abstrahoimalla voimme tarpeen mukaan olla välittämättä yksityiskohdista tai yksittäisistä tapauksista. Esimerkiksi funktio pallonTilavuus kuvaa yleisellä tasolla sitä kaavaa (algoritmia), jolla pallon tilavuuden voi eri tapauksissa laskea.

Olemme jo kohdanneet muitakin abstraktion muotoja kuin funktiot. Esimerkiksi muuttuja on abstraktio: lausekkeessa luku + 1 kuvaamme muuttujan nimellä yleisesti, että haluamme tehdä tietyn laskutoimituksen, kuitenkin ottamatta tässä yhteydessä kantaa siihen yksityiskohtaan, mikä luku-muuttujan arvo on kyseisessä tapauksessa.

Parametrit tekevät funktiosta abstraktimpia. Koska sin-funktio ottaa parametrin (esim. sin(1)), se on voitu laatia välittämättä niistä nimenomaisista tilanteista, joissa funktiota eri ohjelmissa käytetään. Niinpä se on abstraktimpi ja yleishyödyllisempi kuin muuttuja luvunYksiSini olisi.

Voi sanoa, että ohjelmointi on pitkälti hyödyllisten abstraktioiden suunnittelemista, toteuttamista ja käyttämistä. Tähän teemaan palaamme useasti kurssin mittaan (esim. luvut 2.1 ja 3.2). Omien funktioiden toteuttamista pääset harjoittelemaan heti seuraavassa luvussa.

Yhteenvetoa

  • Aliohjelma on toteutus tietylle toiminnolle. Esimerkiksi Scalan yhteydessä aliohjelmia kutsutaan funktioiksi.
  • Funktio voi vaikuttaa ohjelman tilaan tai palauttaa arvon (esim. laskutoimituksen tuloksen) tai tehdä molemmat näistä.
  • Käskyä suorittaa funktio sanotaan funktion kutsumiseksi. Funktiolle voi välittää parametreja sitä kutsuessa.
  • Ohjelmointikielten yhteyteen on määritelty ns. kirjastoja; esimerkiksi Scala-kieleen liittyy yleisiä matemaattisia kirjastofunktioita.
  • Funktiot ovat eräs ohjelmoinnissa käytetty abstraktion muoto. Abstraktiot auttavat ohjelmoijaa työstämään monimutkaisiakin kokonaisuuksia ja vähentävät turhaa työtä.
  • Unit on erikoisarvo, joka merkitsee vain "ei merkityksellistä arvoa".
  • import-käsky on kätevä, kun haluat käyttää jonkin pakkauksen sisältämää työkalustoa.
    • REPL-ympäristömme hoitaa kaikkein tyypillisimmät import-käskyt automaattisesti, mutta REPLissäkin sinun on itse joskus importattava pakkauksia (kuten scala.math) käyttöön.
  • Lukuun liittyviä termejä sanastosivulla: funktio / aliohjelma; funktiokutsu, parametrilauseke, palautusarvo; vaikutukseton funktio, vaikutuksellinen funktio; yksikkötyyppi eli Unit; kirjasto; abstraktio.

Edellisen luvun käsitekaavio funktioilla höystettynä:

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, Jaakko Kantojärvi, Niklas Kröger, Teemu Lehtinen, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä 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 tällä hetkellä 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 ovat luoneet Nikolai Denissov, Olli Kiljunen, Nikolas Drosdek, Styliani Tsovou, Jaakko Närhi ja Paweł Stróżański yhteistyössä Juha Sorvan, Otto Seppälän, Arto Hellaksen ja muiden kanssa.

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

Lisäkiitokset tähän lukuun

Luvussa tehdään vääryyttä Neal Heftin, Mike Oldfieldin ja Dave Stewartin säveltämälle musiikille. Kiitos ja anteeksi.

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