Ohjelmien pilkkominen pienempiin osiin
Learning objectives
- Tutustut käsitteisiin funktio ja kutsupino.
- Kertaat aiemmin opittuja asioita: muuttujat, laskeminen, ehtolause, toistolause.
- Opit pilkkomaan ohjelmia pienempiin osiin funktioiden avulla.
Tähän mennessä olemme tutustuneet useaan ohjelmoinnissa käytettävään perustyövälineeseen. Olemme harjoitelleet lukemista ja syötteen käsittelyä, muuttujien käyttöä tiedon säilömiseen sek ä laskentaan, vaihtoehtoisen toiminnallisuuden luomista, sekä asioiden toistamista. Toteuttamamme ohjelmat ovat kirjoitettu tähän mennessä yhteen pötköön ohjelman main-funktion lohkoon.
Toteuttamalla kaiken toiminnallisuuden osaksi "pääohjelmaa" ohjelma paisuu, mikä voi johtaa ohjelman ymmärrettävyyden heikentymiseen. Tarkastellaan seuraavaksi ohjelman pilkkomista pienempiin osiin funktioiden avulla. Kertaamme samalla oikeastaan kaikkea tähän mennessä oppimaamme.
Funktiot
Funktio (function) on ohjelmoijan määrittelemä (tai ohjelmointikielen valmiina tarjoama) osaohjelma, joita voi kutsua. Funktion kutsuminen suorittaa funktioon liittyvään lähdekoodin. Funktiolla on aina nimi ja funktion runko, joka sisältää lähdekoodia. Funktiota kutsutaan sen nimen perusteella ja funktion kutsuminen suorittaa funktion rungon.
Alla olevassa esimerkissä on kaksi funktiota: (1) funktio main, josta ohjelmamme suoritus alkaa, ja (2) funktio tervehdi, jonka kutsuminen tulostaa viestin Hei funktiomaailma!.
Funktiosta main kutsutaan funktiota tervehdi.
main() {
tervehdi();
}
tervehdi() {
print('Hei funktiomaailma!');
}Kun ohjelma suoritetaan, ohjelman tulostus on seuraavanlainen
program output
Hei funktiomaailma!
Funktion määrittely alkaa funktion nimestä -- yllä nimeksi on määritelty tervehdi. Tätä seuraa sulut () sekä funktioon liittyvä lohko, joka sisältää lähdekoodin, joka suoritetaan kun funktiota kutsutaan. Funktion kutsuminen tapahtuu kirjoittamalla ohjelmaan funktion nimi, sulut, sekä puolipiste -- esimerkiksi tervehdi-funktion kutsuminen tapahtuu muodossa tervehdi();.
Ohjelmien aloituskohta main() on myös funktio. Toisin kuin muut funktiot, funktiota main ei tarvitse kutsua. Kun käynnistämme ohjelman, funktiota main kutsutaan automaattisesti ja ohjelman suoritus alkaa.
Funktioita voi määritellä käytännössä rajattoman määrän. Alla olevassa esimerkissä funktio tervehdi kutsuu funktiota viiva, joka tulostaa viivoja.
program output
------------------- Hei funktiomaailma! -------------------
Funktion parametrit
Edellä käsitellyt funktiot suorittavat funktion lohkoon määritellyn ohjelmakoodin, jonka jälkeen funktion suoritus loppuu. Funktion toimintaan ei ole voinut vaikuttaa ja toiminnallisuus on ollut jokaisella kutsukerralla sama.
Tutustutaan seuraavaksi funktioiden toimintaan vaikuttavien ohjeistusten eli parametrien määrittelyyn.
Parametrit (parameter) ovat funktioon liittyviä muuttujia, joiden arvot annetaan funktiolle funktiokutsun yhteydessä. Parametrit määritellään funktion nimen määrittelyn jälkeen tulevien sulkujen sisälle. Alla olevassa esimerkissä määritellään funktio tervehdi, jolla on parametri nimi.
tervehdi(nimi) {
print('Hei $nimi');
}Parametrillisen funktion kutsuminen poikkeaa parametrittoman funktion kutsumisesta siinä, että parametrillista funktiota kutsuttaessa funktiokutsussa funktiolle annetaan parametrien arvot.
Yksiparametrisen tervehdi-funktion kutsuminen tapahtuu siten, että kutsun yhteydessä funktioon määritellylle parametrille annetaan arvo, joka on käytettävissä funktion tervehdi suorituksessa. Funktion tervehdi sisällä parametria nimi voi käyttää kuten mitä tahansa muutakin muuttujaa. Alla olevassa esimerkissä funktiota tervehdi kutsutaan kahdesti.
main() {
tervehdi('Leibniz');
tervehdi('Boole');
}
tervehdi(nimi) {
print('Hei $nimi');
}Ohjelman tulostus on seuraava.
program output
Hei Leibniz Hei Boole
Alla olevassa esimerkissä määritellään funktio tulostaLukuunAsti, joka tulostaa luvut nollasta parametrina annettuun lukuun asti.
tulostaLukuunAsti(lukuun) {
for (var i = 0; i <= lukuun; i++) {
print('$i');
}
}Kuten aiemmin, parametrin arvo annetaan kunkin funktiokutsun yhteydessä. Alla olevassa esimerkissä tulostetaan ensin luvut nollasta kolmeen ja sitten nollasta viiteen.
main() {
tulostaLukuunAsti(3);
print('Uudestaan!');
tulostaLukuunAsti(5);
}
tulostaLukuunAsti(lukuun) {
for (var i = 0; i <= lukuun; i++) {
print('$i');
}
}program output
0 1 2 3 Uudestaan! 0 1 2 3 4 5
Question not found or loading of the question is still in progress.
Funktiolle määriteltävien parametrien määrää ei ole rajattu. Mikäli funktiolle määritellään useampi kuin yksi parametri, erotetaan parametrit toisistaan pilkuilla.
Alla olevassa esimerkissä luodaan kaksiparametrinen funktio tulostaLuvutValilla, joka tulostaa luvut annetulla välillä. Funktiolle annetaan kaksi parametria: alku ja loppu. Funktiosta main kutsutaan funktiota tulostaLuvutValilla kahdesti.
main() {
tulostaLuvutValilla(3, 5);
print('--');
tulostaLuvutValilla(0, 1);
}
tulostaLuvutValilla(alku, loppu) {
for (var i = alku; i <= loppu; i++) {
print('$i');
}
}Yllä olevan ohjelman tulostus on seuraava.
program output
3 4 5 -- 0 1
Huomaa, että funktiokutsun yhteydessä annetut parametrit annetaan funktiolle kutsun järjestyksessä. Esimerkiksi yllä kutsu tulostaLuvutValilla(3, 5); johtaa siihen, että funktion tulostaLuvutValilla muuttujaan alku kopioidaan arvo 3 ja muuttujaan loppu kopioidaan arvo 5. Tämän jälkeen funktion sisältämä lähdekoodi suoritetaan.
Vastaavasti kutsu tulostaLuvutValilla(0, 1); johtaa siihen, että funktion tulostaLuvutValilla muuttujaan alku kopioidaan arvo 0 ja muuttujaan loppu kopioidaan arvo 1, jonka jälkeen funktion koodi suoritetaan.
Alla on esiteltynä funktio tervehdiMontaKertaa. Funktion kutsuminen tulostaa ensimmäisen parametrin arvon toisen parametrin arvon määräämän kertamäärän.
main() {
tervehdiMontaKertaa('Boole', 2);
}
tervehdiMontaKertaa(nimi, kertaa) {
for (var i = 0; i < kertaa; i++) {
print('Hei $nimi!');
}
}Yllä oleva ohjelman tulostus on seuraava.
program output
Hei Boole! Hei Boole!
Mikäli parametrit antaa funktiolle tervehdiMontaKertaa väärässä järjestyksessä, ei ohjelma toimi halutulla tavalla. Esimerkiksi kutsu tervehdiMontaKertaa(2, 'Boole'); aiheuttaa virheen Uncaught Error: TypeError: "Boole":.
Arvon palauttaminen funktiosta
Tähän mennessä toteuttamamme funktiot sisältävät toiminnallisuutta, joka suoritetaan funktiota kutsuttaessa. Funktioissa oleva toiminnallisuus on rajoittunut funktioiden sisälle, eikä esimerkiksi niissä tapahtuvasta laskennasta ole annettu tietoa funktion ulkopuolelle.
Funktioista voi palauttaa arvon return-komennolla. Alla olevassa esimerkissä on kuvattu funktio summa, joka saa parametrinaan kaksi lukua. Funktio summa laskee parametrina saamiensa lukujen summan ja palauttaa sen funktion kutsujalle.
Ohjelman tulostus on seuraavanlainen.
program output
5 42
Mikäli funktion palauttamaa arvoa haluaa käyttää osana ohjelmaa, tulee se säilöä muuttujaan. Alla olevassa esimerkissä summa-funktion palauttama arvo asetetaan muuttujaan, jonka arvo myöhemmin tulostetaan.
Ohjelman tulostus on seuraavanlainen.
program output
Kutsu summa(3, 2); palautti arvon 5
Arvon palauttavat funktiot ovat hyödyllisiä ohjelman toiminnan jakamisessa pienempiin osiin. Alla olevassa ohjelmassa on toteutettu funktio, jolla voi kysyä lukuja annetulta väliltä. Mikäli käyttäjä syöttää jonkun muun luvun, ohjelma pyytää käyttäjää syöttämään uuden luvun. Funktiota käytetään pituuden ja iän kysymiseen.
Mikäli alla kuvattua funktiota ei olisi, lukemiseen käytetty lähdekoodi olisi pitänyt kopioida ohjelmaan kahdesti -- kerran kumpaakin syötettä varten.
program output
Pituutesi? Syötä välillä [0, 272] oleva luku. 167 Ikäsi? Syötä välillä [0, 117] oleva luku. -3 Epäkelpo luku. Syötä välillä [0, 117] oleva luku. 19 Kiitos tiedoista! Olet 167 senttiä pitkä ja 19 vuotta vanha.
Funktioiden suoritusjärjestys
Mitä funktioita sisältävän ohjelman suorituksessa oikein tapahtuu ja mistä tietokone tietää mistä seuraavaksi jatkaa? Tarkastellaan tätä seuraavan tervehdyksen tulostavan esimerkin kautta.
main() {
tervehdi();
}
tervehdi() {
print('Hei funktiomaailma!');
viiva();
}
viiva() {
print('-------------------');
}program output
Hei funktiomaailma! -------------------
Ohjelman suorittamisesta vastuussa oleva ohjelma tarvitsee tietoa suoritettavasta ohjelmasta, jotta ohjelman suoritus voi edetä toivotulla tavalla. Tämä tieto sisältää käytössä olevat muuttujien arvot, tällä hetkellä suoritettavan lähdekoodirivin ja funktion, sekä nämä tiedot myös niistä funktioista, joita kutsumalla on päädytty nykyiseen funktioon. Tätä tietokokoelmaa kutsutaan kutsupinoksi.
Kutsupino (call stack) sisältää joukon kehyksiä (stack frame), joista kukin sisältää tiedon suoritettavana olevasta funktiosta sekä siinä käytössä olevista muuttujista. Yllä olevan ohjelman suorituksessa kutsupinoa hyödynnetään seuraavan videon kuvaamalla tavalla.
YouTube-video
Tämä sivusto käyttää YouTube-videoita. Lue lisätietoja YouTube-videoiden käytöstä YouTuben Terms of Service-sivulla. Salli YouTube-videoiden näyttäminen, jotta voit katsoa videoita.
Ohjelman suorituksesta vastuussa oleva ohjelma pitää tarkkaan kirjaa kullakin hetkellä suoritettavasta funktiosta, funktion rivistä, sekä aiemmin suoritetuksen alla olleista funktioista. Jokaisessa kehyksessä -- vaikkei edellisessä esimerkissä sitä käsitelty -- on myös tieto funktiossa olevista muuttujista sekä niiden arvoista. Mikäli funktiolle on määritelty parametreja, kopioidaan parametrien arvot funktiolle luotavaan kehykseen funktiokutsun yhteydessä.
Question not found or loading of the question is still in progress.
Hi! Please help us improve the course!
Please consider the following statements and questions regarding this part of the course. We use your answers for improving the course.
I can see how the assignments and materials fit in with what I am supposed to learn.
I find most of what I learned so far interesting.
I am certain that I can learn the taught skills and knowledge.
I find that I would benefit from having explicit deadlines.
I feel overwhelmed by the amount of work.
I try out the examples outlined in the materials.
I feel that the assignments are too difficult.
I feel that I've been systematic and organized in my studying.
How many hours (estimated with a precision of half an hour) did you spend reading the material and completing the assignments for this part? (use a dot as the decimal separator, e.g 8.5)
How would you improve the material or assignments?