4.2. ALIOHJELMAT 71 sisältyä kaikki tarvittavat kontrollia ohjaavat rakenteet. Jos se on lause (yleensä lohko), niin on ratkaistava, miten paluuarvo ilmaistaan. Joissakin kielissä (esimerkiksi Pascal) funktion nimeen sijoittaminen ilmaisee paluuarvon. Toisissa kielissä (kuten C-sukuiset) on erityinen return-lause, jolla funktiosta poistutaan paluuarvo ilmaisten. 4.2.2 Parametrinvälitysmekanismit Olemme puhuneet edellä aliohjelman argumenteista. Kuten edellä sanottiin, ne ovat ne datat, jotka aliohjelmalle annetaan ne ovat lausekkeita, jotka ovat osa aliohjelmakutsua. Asian toinen puoli on se, että aliohjelman sisällä nämä näkyvät paikallisina muuttujina. Näitä muuttujia sanotaan aliohjelman parametreiksi (parameter). Toisin sanoen siinä missä kutsuja näkee argumentteja, kutsuttava näkee parametreja. Toisinaan argumentista käytetään nimeä todellinen parametri (actual parameter), jolloin parametria sanotaan muodolliseksi parametriksi (formal parameter). Sitä mekanismia, jolla argumentti ja parametri kytketään toisiinsa, sanotaan parametrinvälitysmekanismiksi (parameter passing mechanism). Seuraavat neljä mekanismia ovat tavallisimmat: Arvovälitteisyys (call by value) Arvovälitteinen parametri on kopio argumentista: parametrin arvo on aliohjelman alussa argumentin arvo, ja parametriin tehdyt muutokset eivät näy argumentissa. Tulosvälitteisyys (call by result) Tulosvälitteinen parametri kopioidaan lopuksi argumentiksi: parametrin arvo on aliohjelman alussa määräämätön, mutta siihen tehdyt muutokset välitetään argumenttiin kopioimalla parametri argumenttiin aliohjelman palatessa. Arvo-tulosvälitteisyys (call by value-result) Arvo-tulosvälitteinen parametri on kopio argumentista, ja se kopioidaan lopuksi argumentiksi: parametrin arvo on aliohjelman alussa argumentin arvo, ja parametriin tehdyt muutokset välitetään argumenttiin kopioimalla parametri argumenttiin aliohjelman palatessa. Viitevälitteisyys (call by reference) Argumentti ja parametri ovat sama olio, joten kaikki parametriin tehdyt muutokset näkyvät välittömästi argumentissa. Nykyaikana näistä tavallisimmat ovat arvovälitteisyys ja viitevälitteisyys. Harvinaisempi ja nykyisin huonoksi todettu mekanismi on nimivälitteisyys (call by name). Idea on se, että argumenttilauseketta ei lasketa kutsuhetkellä, vaan ohjeet sen laskemiseksi annetaan aliohjelmalle. Kun aliohjelma käyttää parametria, se laskee argumenttilausekkeen arvon. Tämä tehdään uudestaan
72 LUKU 4. KONTROLLI joka kerta, kun parametria käytetään. Sivuvaikutusten kanssa tällainen voi aiheuttaa kaikenlaista vinkeää (etsipä jostain käsiisi tietoa vekottimesta nimeltä Jensen s device) ja myös ikävää (koetapa kirjoittaa toimiva swap-aliohjelma). Toimivampi mutta varsin harvinainen versio nimivälitteisyydestä on nimeltään tarvevälitteisyys (call by need). Idea on sama kuin nimivälitteisyydessä, mutta tarvevälitteisyydessä huolehditaan siitä, että jokainen tarvevälitteinen parametri lasketaan samalla aktivaatiokerralla korkeintaan kerran seuraavilla käyttökerroilla käytetään aiemman laskennan tulosta. Tarvevälitteisyyttä kutsutaan toisinaan laiskaksi laskennaksi (lazy evaluation), ja sitä käytetään väljien funktioiden toteuttamisessa. 4.2.3 Aktivaatiotietue Pinoon aliohjelmakutsun yhteydessä rakentuva tietue (joka siis sisältää aliohjelman parametrit paluuosoite mukaanlukien) on nimeltään aktivaatiotietue (activation record) tai kehys (frame). Jokainen kerta, kun aliohjelmaa kutsutaan, syntyy aliohjelman uusi aktivaatio (activation), ja sen aktivaatiotietue sisältää kaiken sen tiedon, mitä tuosta nimenomaisesta aktivaatiotiedosta on pidettävä yllä ja erillään muista aktivaatioista. Aliohjelma itse lisää siihen tilan paikallisille, pinodynaamisille muuttujilleen. Joissakin tilanteissa aktivaatiotietue on staattinen olio; tällöin kyseinen aliohjelma ei voi olla rekursiivinen eikä vapaakäyntinen (reentrant). Joissakin kielten toteutuksissa aktivaatiotietue on kekodynaaminen olio joskus tämä on jopa välttämätöntä. Aktivaatiotietueen paikka ei ole tavallisesti tiedossa ennen suorituksen alkua (se on tiedossa vain, jos aktivaatiotietue on staattinen olio). Sen sijaan sen osien sijainti suhteessa tietueen alkuun on hyvinkin tiedossa ennen suorituksen alkua. Senpä takia pääsy aktivaatiotietueessa oleviin tietoihin järjestetään käyttäen lähes jokaisesta prosessorista löytyvällä rekisteri-ja-siirtymä-osoituksella. Kielen toteutus varaa yhden rekisterin osoittamaan sen aliohjelman aktivaatiotietuetta, jolla kontrolli on. Tätä rekisteriä sanotaan kehysosoitinrekisteriksi (frame (pointer) register) tai kantaosoitinrekisteriksi (base pointer register). Kun jotain toista aliohjelmaa kutsutaan, tulee tuon rekisterin sisältö tallentaa jonnekin. Käytännössä se tallennetaan aliohjelman alkaessa kutsutun aliohjelman omaan aktivaatiotietueeseen, ja se palauttaa rekisterin arvon ennen paluutaan. Tätä tallennettua kehysosoitinrekisterin arvoa sanotaan dynaamiseksi linkiksi (dynamic link) sitä voitaisiin käyttää myös esimerkiksi dynaamisen vaikutusalueen toteuttamiseen. Kaikki ne aktivaatiotietueet, joihin pääsee dynaamisten linkkien kautta siitä tietueesta, jonka osoite on kehysosoitinrekisterissä, muodostavat kutsupinon (call stack) eli aktivaatiopinon (activation stack) eli dynaamisen ketjun (dynamic chain).
4.2. ALIOHJELMAT 73 Joissakin kielissä aliohjelmia voidaan kirjoittaa toisten aliohjelmien sisään. Mikäli tällaisessa kielessä on käytössä staattinen vaikutusalue, tulee jotenkin aliohjelmasta olla pääsy sitä staattisesti ympäröivien aliohjelmien sopivan aktivaation paikallisiin muuttujiin (eli aktivaatiotietueisiin). Yksinkertaisessa tapauksessa tällaista aliohjelmaa voidaan kutsua vain itsestään, siitä aliohjelmasta, jossa se on määritelty, sekä niistä aliohjelmista, jotka on määritelty sen itsensä sisällä (staattisessa mielessä). Tällaisessa tilanteessa luonteva valinta staattisesti ympäröivän aliohjelman aktivaatioksi, johon pitää päästä käsiksi, on se, jossa kutsu tapahtuu. Kun kutsu tapahtuu, annetaan aliohjelmalle piilotettuna argumenttina osoitin tähän aktivaatiotietueeseen. Tällöin tuo osoite jää osaksi kutsutun aliohjelman aktivaatiotietuetta; tuota osoitetta sanotaan staattiseksi linkiksi (static link). Aktivaatiotietueet, joihin pääsee staattisten linkkien kautta siitä tietueesta, jonka osoite on kehysosoitinrekisterissä, muodostavat staattisen ketjun (static chain). Staattisen vaikutusalueen ollessa voimassa mihin tahansa näkyvään paikalliseen muuttujaan pääsee käsiksi hakemalla sitä staattisen ketjun kautta. Kussakin tilanteessa tiedetään ennen suorituksen alkua, kuinka syvällä staattisessa ketjussa kukin muuttuja on, ja tämän perusteella voidaan muuttujanhakukoodi generoida suoraviivaisesti: ladataan johonkin rekisteriin ensin kehysosoitinrekisterin arvo, sitten ladataan tuon osoittimen kautta staattinen linkki tuohon rekisteriin ja iteroidaan tätä tarpeeksi monta kertaa. Lopuksi tuota rekisteriä voidaan käyttää kuin kehysosoitinrekisteriä ikään hakemaan löydetyn aktivaatiotietueen sisältämä muuttuja. Erityisesti 1970- ja 1980-luvun kääntäjät yrittivät optimoida tätä näyteikkunatekniikalla (display technique). Tässä pidetään yllä erityistä taulukkoa, johon on koottu kaikki staattiseen ketjuun kuuluvat staattiset linkit. Tällöin muuttujan haku vaatii tämän näyteikkunataulukon (display) indeksoinnin ja sitten tuloksena saadun osoitteen käyttämisen kuten edellä muuttujan hakemiseen. Iterointi jää kokonaan pois. Käytännössä tästä ei kuitenkaan ole ollut hyötyä, koska näyteikkunan ylläpito vaatii yleensä enemmän työtä kuin mitä muuttujanhaussa säästyy: yleensä muuttujanhakuja muualta kuin omasta aktivaatiotietueesta on harvoin, ja silloinkin kun niitä on usein, ne voidaan optimoida tehokkaammin yleisemmillä tekniikoilla, esimerkiksi yhteisen alilausekkeen optimoinnilla. Silti näyteikkunatekniikka voi olla harkinnan arvoinen prosessoreilla, joissa on vähän rekistereitä. Mikäli aliohjelma voidaan antaa argumenttina toiselle aliohjelmalle, monimutkaistuu staattisen linkin hakeminen. Tällöin käytännössä argumenttina antajan (joka on siinä asemassa, kuin edellä oli aliohjelman kutsuja) tulee antaa argumenttina (piilossa) myös staattinen linkki, jotta sen saaja voisi kutsua alioh-
74 LUKU 4. KONTROLLI jelmaa antaen sille tuo staattinen linkki (omaansa se ei yleisessä tapauksessa voi antaa). Joissakin kielissä aliohjelmat ovat täysivaltaisia (first class), eli niitä voidaan tallentaa muuttujiin, viedä parametrina ja palauttaa paluuarvona vapaasti. Tällöin aliohjelmalla tulee olla olio, joka sitä edustaa muuttujissa, aliohjelman paluuarvona ja argumenttina. Mikäli kielessä aliohjelmat eivät voi olla sisäkkäisiä, pelkkä aliohjelman koodin alun osoite riittää olion sisällöksi. Näin toimitaan esimerkiksi C:ssä (funktio-osoitin). Muussa tapauksessa oliossa pitää tuon osoittimen lisäksi olla myös staattinen linkki, joka annetaan kutsuttaessa aliohjelmalle piiloargumenttina. Tällaista oliota sanotaan toisinaan sulkeumaksi (closure). 4.2.4 Vuorottaisrutiinit Sukua aliohjelmakäsitteelle on sellainen käsite kuin vuorottaisrutiini (coroutine). Vuorottaisrutiinia kutsutaan kuten aliohjelmaa, ja siinä syntyy vastaavalla tavalla aktivaatiotietue. Ero on siinä, että vuorottaisrutiini voi palauttaa kontrollin väliaikaisesti kutsujalleen; tällöin kutsuja voi toimia, kunnes se antaa kontrollin takaisin vuorottaisrutiinille, jolloin sen suoritus jatkuu siitä mihin se jäi. Tällainen pallottelu voi jatkua pitkäänkin, kunnes vuorottaisrutiini palaa aliohjelman tavoin lopullisesti. 4.2.5 Metodit OO-olion etuoikeutetut aliohjelmat, metodit (methods), eroavat tavallisista aliohjelmista kahdella tavalla: aliohjelman nimi sidotaan aliohjelman koodiin vasta ajon aikana, riippuen kohdeolion tyypistä eikä niinkään sitä edustavan muuttujan tyypistä, ja aliohjelmalle annetaan kohdeolio piilotettuna parametrina. Metodin sidonta (method binding) kirjoitetaan tavallisesti syntaksilla, jossa kohdeoliota edustava lauseke on pisteoperaattorin (tai joskus jonkin muun operaattorin) vasen operandi ja metodin nimi on oikea operandi. Syntaksista riippumatta sen merkitys on kuitenkin sama. Tämän sidontalausekkeen tuloksena on aliohjelma-arvo, joka sisältää olion itseviitteen ja viitteen metodin koodiin. Kun tätä kutsutaan, itseviite tulee piiloparametriksi. Metodin sidonta tapahtuu tavallisesti siten, että OO-olioon liittyy viittaus luokkaolioon, jossa on metoditaulu (method table, vtable), joka sisältää koodien osoitteita. Kuhunkin metodiin liittyy indeksi taulukkoon. Kun metodin sidonta tapahtuu, indeksoidaan kohdeolion luokkaolion metoditaulua metodin indeksillä, ja metodin koodiksi valitaan se, jonka osoite näin löytyy.
4.2. ALIOHJELMAT 75 On hyvä huomata, että sidontaoperaation tulos muistuttaa huomattavasti sulkeumaa. Samalla tulee huomatuksi, että itseviite muistuttaa kovasti staattista linkkiä. Jos tätä analogiaa halutaan viedä vielä pidemmälle, päädytään ajatukseen, jossa OO-olio itse on jonkin aliohjelman aktivaatiotietue tämä aliohjelma lienee sitten olion luokka, mutta se ei oikeastaan ole aliohjelma vuorottaisrutiini: kun olio luodaan, luokka eli vuorottaisrutiini käynnistyy (paikalliset muuttujat saavat arvonsa; tämä vastaa rakenninta), minkä jälkeen vuorottaisrutiini palauttaa kontrollin olion luontia pyytäneelle; kun vuorottaisrutiinille palautetaan kontrolli, se tekee mahdolliset purkamistoimenpiteet. Toisaalta analogia voidaan kääntää ylösalaisin: aliohjelma voidaan ajatella OOolioksi: aktivaatiotietue on OO-olio, ja aliohjelman sisällä määritellyn aliohjelman kutsu vastaa metodikutsua. Tehtävä 8 Onko aliohjelma OO-olio vai OO-olio aliohjelma? Vai ovatko ne jonkin kolmannen käsitteen ilmentymiä?