TIES542 kevät 2009 Kontrollivuon ohjaus

Samankaltaiset tiedostot
4.2. ALIOHJELMAT 71. Tulosvälitteisyys (call by result) Tulosvälitteinen parametri kopioidaan lopuksi

kontrollivuon analyysejä optimointiensa tueksi ja myös tiettyjen merkitysopillisten

Aliohjelmat. 1 Kutsusekvenssit. Antti-Juhani Kaijanaho 5. helmikuuta 2007

järjestys, jossa ohjelman suoritus etenee ohjelmatekstissä jollakin tietyllä suorituskerralla (dynaaminen kontrollivuo)

Ohjelmointikielten periaatteet Syksy Antti-Juhani Kaijanaho

11/20: Konepelti auki

Jatkeet. TIES341 Funktio ohjelmointi 2 Kevät 2006

Algoritmit 1. Luento 3 Ti Timo Männikkö

Java-kielen perusteita

ITKP102 Ohjelmointi 1 (6 op)

Ehto- ja toistolauseet

TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 13. lokakuuta 2009

Ohjelmoinnin peruskurssien laaja oppimäärä

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

Aliohjelmatyypit (2) Jakso 4 Aliohjelmien toteutus

811120P Diskreetit rakenteet

811120P Diskreetit rakenteet

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

11. Javan toistorakenteet 11.1

TIE PRINCIPLES OF PROGRAMMING LANGUAGES Eiffel-ohjelmointikieli

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit

Lisää pysähtymisaiheisia ongelmia

Ohjelmoinnin peruskurssien laaja oppimäärä

Jakso 4 Aliohjelmien toteutus

Luento 4 Aliohjelmien toteutus

Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet suoritetaan peräkkäin.

Luku 7. Aliohjelmat. 7.1 Kutsusekvenssit. Aliohjelma (subroutine) on useimpien kielten tärkein kontrollivuon ohjausja abstrahointikeino.

5/20: Algoritmirakenteita III

ATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014

Jakso 4 Aliohjelmien toteutus

Kehittää ohjelmointitehtävien ratkaisemisessa tarvittavia metakognitioita!

12. Javan toistorakenteet 12.1

Tutoriaaliläsnäoloista

2) Aliohjelma, jonka toiminta perustuu sivuvaikutuksiin: aliohjelma muuttaa parametrejaan tai globaaleja muuttujia, tulostaa jotakin jne.

Luento 4 (verkkoluento 4) Aliohjelmien toteutus

Osoitin ja viittaus C++:ssa

ITKP102 Ohjelmointi 1 (6 op)

Vasen johto S AB ab ab esittää jäsennyspuun kasvattamista vasemmalta alkaen:

Luento 5. Timo Savola. 28. huhtikuuta 2006

Tietotekniikan valintakoe

Aliohjelmatyypit (2) Jakso 4 Aliohjelmien toteutus

Muistutus aikatauluista

13. Loogiset operaatiot 13.1

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 30. marraskuuta 2015

12. Javan toistorakenteet 12.1

Luento 4 (verkkoluento 4) Aliohjelmien toteutus

Se mistä tilasta aloitetaan, merkitään tyhjästä tulevalla nuolella. Yllä olevassa esimerkissä aloitustila on A.

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

jäsentäminen TIEA241 Automaatit ja kieliopit, syksy 2015 Antti-Juhani Kaijanaho 26. marraskuuta 2015 TIETOTEKNIIKAN LAITOS

Imperatiivisten ohjelmien organisointiparadigmojen. historia

Imperatiivisten ohjelmien organisointiparadigmojen historia

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

Jakso 4 Aliohjelmien toteutus

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

f(n) = Ω(g(n)) jos ja vain jos g(n) = O(f(n))

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssi Y1

TIEA341 Funktio-ohjelmointi 1, kevät 2008

Ohjelmoinnin perusteet Y Python

jäsennyksestä TIEA241 Automaatit ja kieliopit, syksy 2016 Antti-Juhani Kaijanaho 29. syyskuuta 2016 TIETOTEKNIIKAN LAITOS Kontekstittomien kielioppien

samalla seuraavaan puoliavaruuteen (sukupolveen), jota siivotaan harvemmin.

Ohjelmoinnin peruskurssien laaja oppimäärä

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

Johdanto II. TIE303 Formaalit menetelmät, kevät Antti-Juhani Kaijanaho. Jyväskylän yliopisto Tietotekniikan laitos.

3. Muuttujat ja operaatiot 3.1

1. Omat operaatiot 1.1

Jakso 4 Aliohjelmien toteutus. Tyypit Parametrit Aktivointitietue (AT) AT-pino Rekursio

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 12. tammikuuta 2012

Tieto ja sen osoite (3) Jakso 3 Konekielinen ohjelmointi (TTK-91, KOKSI) Osoitinmuuttujat. Tieto ja sen osoite (5)

Tietorakenteet ja algoritmit

Vertailulauseet. Ehtolausekkeet. Vertailulauseet. Vertailulauseet. if-lauseke. if-lauseke. Javan perusteet 2004

13. Loogiset operaatiot 13.1

ITKP102 Ohjelmointi 1 (6 op)

Sisällys. 3. Muuttujat ja operaatiot. Muuttujat ja operaatiot. Muuttujat. Operaatiot. Imperatiivinen laskenta. Muuttujat. Esimerkkejä: Operaattorit.

Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005

TIEA341 Funktio-ohjelmointi 1, kevät 2008

TIES542 kevät 2009 Tyyppijärjestelmän laajennoksia

Java-kielen perusteet

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 5. marraskuuta 2015

Ohjelmointikieli TIE Principles of Programming Languages Syksy 2017 Ryhmä 19

Hohde Consulting 2004

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

Luento 4 Aliohjelmien toteutus

CODEONLINE. Monni Oo- ja Java-harjoituksia. Version 1.0

Yhtälönratkaisusta. Johanna Rämö, Helsingin yliopisto. 22. syyskuuta 2014

tään painetussa ja käsin kirjoitetussa materiaalissa usein pienillä kreikkalaisilla

Attribuuttikieliopit

Sisällys. 12. Javan toistorakenteet. Yleistä. Laskurimuuttujat

ELM GROUP 04. Teemu Laakso Henrik Talarmo

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 3. lokakuuta 2016

Apuja ohjelmointiin» Yleisiä virheitä

Algoritmit 1. Luento 4 Ke Timo Männikkö

Zeon PDF Driver Trial

TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 26. lokakuuta 2009

Approbatur 3, demo 1, ratkaisut A sanoo: Vähintään yksi meistä on retku. Tehtävänä on päätellä, mitä tyyppiä A ja B ovat.

TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 27. lokakuuta 2009

Luento 4 Aliohjelmien toteutus

Jakso 3 Konekielinen ohjelmointi (TTK-91, KOKSI)

Transkriptio:

TIES542 kevät 2009 Kontrollivuon ohjaus Antti-Juhani Kaijanaho 20. tammikuuta 2009 Sisältö 1 Testit 2 2 Käskykielten kontrollinohjaus 3 2.1 Valintalauseet 4 2.2 Toistolauseet eli silmukat 5 2.3 Vahtikomennot 7 2.4 Väitteet 9 3 Funktiokielten kontrollinohjaus 10 4 Logiikkakielten kontrollinohjaus 10 5 Aliohjelmat 11 5.1 Kutsusekvenssit 11 5.2 Parametrinvälitysmekanismit 13 5.3 Aktivaatiotietue 14 5.4 Vuorottaisrutiinit 16 5.5 Metodit 17 Useimmat nykykielet ovat luonteeltaan peräkkäistäviä (sequential): ohjelmatekstissä mainitut tehtävät tehdään yksi kerrallaan toinen toisensa jälkeen. Sitä järjestystä, jossa tehtävät tehdään jollakin tietyllä suorituskerralla, sanotaan kontrollivuoksi (control flow). Sillä tarkoitetaan toisinaan myös sitä, missä kaikissa järjestyksissä asiat voidaan tehdä, kun tarkastellaan kaikkia mahdollisia suorituskertoja. Sekaannuksen välttämiseksi ensimmäistä merkitystä kutsutaan tässä monisteessa dynaamiseksi kontrollivuoksi tai lyhyesti kontrolliksi ja jälkimmäistä staattiseksi kontrollivuoksi. Kääntäjät tekevät usein laajoja staattisen kontrollivuon analyysejä optimointiensa tueksi ja myös tiettyjen merkitysopillisten ongelmien (mm. käyttämättömät muuttujat) tunnistamiseksi. 1

Jotkin kielet tukevat yhtäaikaisuutta (concurrency), jolloin ohjelmassa on useita rinnakkaisia dynaamisia kontrollivoita. Tällöinkin ohjelmalla on vain yksi staattinen kontrollivuo, joka kuvaa yhteisesti ohjelman kaikkien suorituskertojen kaikkia dynaamisia kontrollivoita. Jokaisessa ohjelmointikielessä on konstruktioita, joiden tehtävänä on ohjata dynaamisen kontrollivuon kulkua. Tässä monisteessa kutsutaan näitä konstruktioita kontrollia ohjaaviksi (tälle ei ole tietääkseni englanninkielistä termiä) ja näiden konstruktioiden käyttöä kontrollinohjaukseksi. Suoritusaikana on kullakin hetkellä kaksi merkittävää tietoa: missä nyt ollaan ja minne seuraavaksi mennään. Jälkimmäistä sanotaan jatkeeksi (continuation). Koko staattinen kontrollivuo voidaan mallittaa kertomalla, mitä missäkin ohjelman paikassa tehdään ja mikä on siinä jatke. 1 Testit Kontrollivuon ohjaus perustuu siihen, että ohjelma pystyy havainnoimaan omaa tilaansa ja tekemään sen perusteella päätöksiä. Tämän perustana on testit: Kokonaislukutyyppisten ja liukulukutyyppisten lausekkeiden arvojen vertailu (pienempi-kuin, suurempi-kuin, yhtäsuuri-kuin ym.). Taulukoiden vertailu alkioittain. Koosteiset testit, jotka muodostuvat useista testeistä, jotka on yhdistetty loogisin operaattorein. Testit esitetään abstraktissa syntaksissa yleensä lausekkeina. Tätä varten pitää lisätä yksi uusi tyyppi, totuusarvotyyppi (engl. truth value type), yleensä nimeltään boolean. Tässä tyypissä on kaksi arvoa, tosi ja epätosi, jotka syntaksissa esitetään tavallisesti avainsanoina. Tavallisesti loogiset operaattorit (looginen ja, looginen tai) ovat oikosuljettuja (engl. short-circuited), jolloin oikean operandin arvo lasketaan vain, jos koko lausekkeen arvo ei ole selvä jo pelkän vasemman oprandin arvon perusteella. Tämän järjestelyn etuna on, että testeissä voidaan testata jotain sellaista asiaa, joiden laskeminen on jo virhe, kunhan vain ensin samassa testissä rajataan pois tilanteet, joissa tällaiset virheet syntyvät. Esimerkiksi taulukon indeksointi testissä voidaan tehdä, vaikka vasta aiemmin samassa testilausekkeessa on tarkistettu, että indeksi on sallittu. 2

2 Käskykielten kontrollinohjaus Käskykielten merkittävin kontrollia ohjaava konstruktio unohtuu helposti, sillä se on pääasiassa piilossa. Tarkoitan tässä peräkkäistämistä (sequencing), joka ottaa kaksi lausetta järjestyksessä. Kun kontrolli tulee peräkkäistyskonstruktioon (mukanaan jatke ilmaisemassa, mitä normaalitilanteessa pitäisi tehdä tämän konstruktion jälkeen), se antaa jälkimmäiselle osalauseelle jatkeeksi oman jatkeensa ja antaa ensimmäiselle jatkeeksi tuon yhdistelmän. Tälle yhdistelmälle, joka koostuu ensimmäisestä osalauseesta, jonka jatke on toinen osalause, jonka jatkeena on koko konstruktion jatke, konstruktio antaa kontrollin. Toisin sanoen se suorittaa ensin ensimmäisen lauseen ja sitten toisen lauseen. Abstraktissa kieliopissa peräkkäistystä merkitään yleensä lauseiden väliin sijoitetulla puolipisteellä, konkreetissa kieliopissa se jää usein implisiittiseksi. Aikoinaan ohjelmointikielten tärkein kontrollia ohjaava konstruktio oli go to -lause. Se oli pesiytynyt kieliin symbolisten konekielten hyppykäskyistä. Go to ohjasi kontrollin (usein numerolla) nimettyyn paikkaan ohjelmassa. Sen lisäksi käytettiin lähinnä haarautuvaa if-lausetta, joka valitsee lausekkeen arvon perusteella, kumpi sen osalauseista suoritetaan. Tuloksena on vaikeasti ymmärrettävää ohjelmakoodia, ns. spagettikoodia. 1970-luvulla muotia oli rakenteinen ohjelmointi (structured programming), Se oli reaktio spagettikoodia vastaan lääkkeinä nähtiin ylhäältä alas (top-down) -suunnittelu, koodin modularisointi, rakenteiset tyypit, muuttujien selkeä nimeäminen ja laaja kommentointi. Rakenteisen ohjelmoinnin liikkeen yhtenä osana kampanjoitiin Dijkstra [2] etunenässä go to -lauseen hävittämisen puolesta (mutta myös kompromisseja etsittiin [6]). Nykyisin näyttää siltä, että go to on vähintäänkin uhanalainen laji: Java varaa sen avainsanaksi vain siksi, että kääntäjät voisivat antaa parempia virheilmoituksia... Toisaalta niissäkin kielissä, jotka eivät go to -nimistä lausetta sisällä, on sen tapaisia lauseita kuitenkin eri nimillä (Javan break on varsin voimakas heikennetty versio go to -lauseesta; Schemen call-with-current-continuation on sekin hieman rajoitettu mutta erittäin voimakas versio go to -lauseesta). Rakenteisen ohjelmoinnin myötä go to -lauseen tilalle on vakiintunut joukko lohkorakenteisia kontrollia ohjaavia konstruktioita. Jokin for-lauseen muunnelma löytyy kaikista käskykielistä. Samoin kielissä on jokin while-lauseen muunnelma sekä täysin haarautuva if... else-lause. Käskykielissä on käytössä myös jonkinlainen aliohjelmarakenne ja usein myös poikkeustenkäsittely. 3

2.1 Valintalauseet Ohjelmoinnissa on kaksi pääasiallista valintatilannetta: suoritettava lause pitää valita joko jonkin testin totuusarvon mukaan tai sitten jonkin toisen lausekkeen arvon mukaan. Toki jälkimmäinen riittäisi yksin, sillä edellinen on sen erityistapaus, mutta edellinen on niin tärkeä erityistapaus, että sille on oma konstruktionsa. If-lauseen semantiikka on kaikille tuttu: selvitetään annetun testin totuusarvo iflauseen kohdalla ja sitten suoritetaan jompi kumpi annetusta vaihtoehtoisesta lauseesta. If-lauseen ainoa kiinnostusta herättävä piirre on sen konkreetti syntaksi. Aivan vanhimmissa kielissä if-lauseen then- ja else-osassa sai olla enintään yksi lause kummassakin; tällöin yhtään monimutkaisempien rakenteiden ilmaisemisessa tarvittiin konekielisestä ohjelmoinnista tuttu goto-lause. Siitä ei kuitenkaan pidetä korkean tason ohjelmoinnissa, joten moderneissa ohjelmointikielissä if-lauseen then- ja else-lause voivat olla mielivaltaisen monimutkaisia, yleensä lohkoja. Tyypillinen konkreetti syntaksi on Pascalin käyttämän tyyppinen if x 0 then if x < 0 then writeln("negatiivinen") else writeln("nolla") else writeln("positiivinen") tai C:n tyylinen: if (x 0) if (x < 0) puts("negatiivinen\n"); else puts("nolla\n"); else puts("positiivinen\n"); Jos kumpi tahansa näistä kirjoitetaan ilmiselvällä tavalla konkreetiksi kieliopiksi ja else-osan pois jättäminen sallituaan, kuten on tapana esimerkiksi 1 statement: if ( expression ) statement if ( expression ) statement else statement 1. Tässä C-kielen määrittelyssä käytetyssä notaatiossa vaihtoehdot on erotettu toisistaan siten, että ne kirjoitetaan eri riveille. Lisäksi välikesymbolit kirjoitetaan kursiivilla ja päätesymbolit tasalevyisellä kirjasimella. 4

niin tällainen konkreetti kielioppi on moniselitteinen: kumpaan if-lauseeeseen else-osa liittyy lauseessa if (x >= 0) if (x == 0) printf("1"); else printf("0");, eli tulostaako se 0 silloin kun x on negatiivinen vai silloin kun se on positiivinen? Tätä ongelmaa sanotaan orvoksi elseksi (engl. dangling else), ja siihen on useita ratkaisuja: Orvon elsen ongelma juontuu siitä, että if-lauseen loppu ei ole yksikäsitteisesti määrätty. Tämä voidaan korjata niin, että lisätään if-lauseen loppuun jokin lopetusavainsana; tyypillisiä ovat endif, end if ja fi (if väärin päin). Näin toimivat esimerkiksi Bourne Shell ja Algolit. Edellisen ratkaisun tyyliin voidaan tehdä myös niin, että then-lauseen tai jopa myös else-lauseen pitää olla lohko. Tällöin ei myöskään jää epäselväksi, mihin if-lause päättyy. Näin toimii esimerkiksi Perl. Vielä yksi edellisten muunnelma on ilmaista ohjelman lohkorakenne sisennyksillä niin, että sisennyksillä on semanttista merkitystä. Tämän seurauksena kieliopin näkökulmasta jokainen then- ja else-lause on lohko, joten ratkaisu on sukua edelliselle ratkaisulle. Näin toimivat esimerkiksi Python ja Haskell. Yleisin ratkaisu lienee kuitenkin julistaa, että if-else-parit löydetään etsimällä ensin sisimmälle if-lauseelle sitä tekstuaalisesti lähinnä oleva else ja sitten toiseksi sisimmälle if-lauseelle sitä tekstuaalisesti lähinnä oleva vielä käyttämätön else jne. Tämä vastaa sitä, mitä ohjelmoijat yleensä tarkoittavat. If-lause toteuttaa valinnan kahden vaihtoehdon välillä riippuen ehtolauseen tuloksesta. Monesti käyttökelpoisempi on usean vaihtoehdon välillä valitseminen käyttäen valintaperusteena mielivaltaisen (yleensä kokonaislukutyyppisen) lausekkeen arvoa. Tällainen lause on case-lause (C-sukuisissa kielissä switch-case). Case-lauseen rakenne on yksinkertainen. Lause koostuu valintalausekkeesta (engl. discriminator) sekä vähintään yhdestä arvo lause-parista. Lauseen suoritus alkaa laskemalla valintalausekkeen arvo. Jos arvolle löytyy lausepari, kyseinen lause suoritetaan. Yleensä lauseessa on myös mukana oletuslause, joka suoritetaan, jos valintalausekkeen arvoa ei ollut paritettu minkään lauseen kanssa. 2.2 Toistolauseet eli silmukat Imperatiivisen ohjelmoinnin ytimessä on sijoituslauseiden ja peräkkäisyyden lisäksi silmukat (engl. loops) eli toistolauseet (engl. repetition statements). Varsin harvinainen mutta kaikista yleiskäyttöisin rakenne on loop. Sen abstrakti 5

syntaksi on seuraavanlainen 2 : κ ::= loop κ 1 exit if ε κ 2 Tämän silmukan intuitiivinen semantiikka on, että joka iteraatiolla suoritetaan ensin κ 1, sitten katsotaan, onko ε tosi ja jos on, silmukka päättyy, muuten suoritetaan κ 2 ja aloitetaan uusi iteraatio. Varsin tavallinen tilanne, jossa tästä muodosta on hyötyä, on syötteen lukeminen, sillä monissa kielissä tiedon loppuminen saadaan selville yrittämällä lukea syötteen päättymisen jälkeen: loop c getchar; exit if c = EOF ;... end loop Silmukkarakenteista kuuluisin on varmastikin while-rakenne: κ ::= while ε do κ Tarpeellinen on usein myös perinteinen for-silmukka: κ ::= for ι : τ ε 1 to ε 2 step ε 3 do κ joka tavallisesti määritellään kielioppimakeisena while-lauseen perusteella: for ι : τ ε 1 to ε 2 step ε 3 do var ι : τ begin ι ε 1 ; while (ι < ε 2 ) (κ; ι ι + ε 3 ) end Ohjelmointikieltä, jossa on tuki suoraviivaohjelmalle, if-lauseille ja while-lauseille, sanotaan joskus while-kieleksi. Sillä on keskeinen merkitys ohjelmoinnin teoriassa imperatiivisen ohjelmointikielen arkkityyppinä. Tämä johtuu siitä, että whilekielelle on olemassa selkeitä ohjelmien oikeaksi todistamiseen liittyviä formalismeja ja siitä, että while-kieli on yksinkertaisimmasta päästä kieliä, joka on laskennallisesti yhtä ilmaisuvoimainen kuin Turingin kone eli kykenee laskemaan kaikki (yleis)rekursiiviset (engl. general recursive) funktiot 2. Tässä lausetta tarkoittavana välikesymbolina käytetään κ:aa ja vastaavasti lauseketta tarkoittavana välikesymbolina ε:a. Myöhemmin esiintyvät myös päätesymboli ι (nimet) ja välikesymboli τ (tyypit). 6

Vastaavasti kieltä, joka on muuten samanlainen kuin while-kieli paitsi, että whilesilmukan tilalla on for-silmukka ja jossa lisäksi vaaditaan, että silmukkamuuttujaan ei voi sijoittaa silmukan sisällä, sanotaan for-kieleksi. Se ei, yllättäen, ole yhtä ilmaisuvoimainen kuin while-kieli, sillä sillä ei voi tehdä päättymätöntä silmukkaa eikä ylipäätään mitään silmukkaa, jonka iteraatioiden lukumäärä ei ole etukäteen tiedossa. Niinpä for-kieli kykenee laskemaan primitiivirekursiiviset (engl. primitive recursive) funktiot ja on Turingin konetta aidosti heikompi. 2.3 Vahtikomennot Mielenkiintoinen variantti while-kielestä saadaan, kun kaikki if-lauseet ja silmukkarakenteet korvataan Dijkstran vahtikomennoilla [3]: κ ::= if ξ guarded conditional do ξ guarded loop ξ ::=ε κ guard ξ 1 ξ 2 guarded choice Vahtikomento ε κ ilmaisee, että lause κ suoritetaan vain jos ε on tosi. Jos ε on epätosi, vahtikomennon sanotaan olevan suorituskelvoton (engl. infeasible). Vahtikomento ξ 1 ξ 2 valitsee vahtikomennoista ξ 1 ja ξ 2 suoritettavaksi jomman kumman, kuitenkin niin, että suorituskelvotonta ei valita. Käytännössä siis vahtikomento ε 1 κ 1 ε n κ n toimii niin, että se valitsee jonkin sellaisen i:n, jolla vahtilauseke ε i saa arvokseen true, ja suorittaa sitten κ i :n; ja jos sellaista i:tä ei ole, koko vahtikomento on suorituskelvoton. Vahtikomennot tuovat ohjelmointikieleen epädeterminismin (engl. nondeterminism): täsmälleen samalla syötteellä sama ohjelma voi käyttäytyä eri tavoin. Epädeterminismiä on kahdenlaista: enkeliepädeterminismiä (engl. angelic nondeterminism) ja demoniepädeterminismiä (engl. demonic nondeterminism). Näiden kuvaavien nimien takaa löytyy kysymys siitä, miten suoritettava lause valitaan, kun vaihtoehtoja on useita. Enkeliepädeterminismin taustalla on vertauskuva, jossa valinnan tekee enkeli: tehdään siis ohjelman käyttäjän kannalta suotuisin valinta (toisin sanoen, jos valintakohdassa toinen vaihtoehto johtaa virhetilanteeseen ja toinen antaa oikean tuloksen, valitaan se oikeaan tulokseen johtava). Enkeliepädeterminismin toteuttaminen tietokoneohjelmassa vaatii käytännössä sitä, että valintaa tehtäessä arvataan ja jos arvaus osoittautuu myöhemmin vääräksi, valinta tehdään uudestaan. Tällaista toimintaa sanotaan peruutukseksi (engl. backtracking) ja sitä käytetään lähinnä logiikkaohjelmoinnissa (josta hieman tarkemmin alla). Toinen tapa 7

toteuttaa enkeliepädeterminismi on valintatilanteen edessä valita kaikki mahdollisuudet ja suorittaa kaikkia vaihtoehtoja toisistaan riippumatta. Jotkut vaihtoehdot osoittautuvat vääriksi valinnoiksi, jolloin niiden suorittaminen lakkaa; ohjelman lopuksi (toivottavasti) hengissä on ainakin yksi suoritus ja niistä voidaan sitten valita umpimähkään, mikä tulos annetaan ulos (tai, haluttaessa voidaan myös antaa ulos kaikki tulokset). Kumpikaan tapa ei toimi kovin hyvin, jos ohjelmalla on ulospäin näkyviä sivuvaikutuksia. Demoniepädeterminismin vertauskuvana on vastaavasti demoni valintaa tekemässä. Tämä ei ole varsinaisesti enkeliepädeterminismin vastakohta, sillä silloin valittaisiin aina väärin ; pikemminkin kyse on ilkeilevästä demonista, joka saattaa joskus valita oikeinkin, mutta siihen ei voi luottaa. Käytännössä tämä tarkoittaa, että demoniepädeterminismissä ohjelman kirjoittajan tulee olettaa pahin mahdollinen valinta varmuuden vuoksi. Vahtikomentojen epädeterministisyys on käytännössä aina demonista, sillä demoniepädeterminismi on helpoin toteutettava. Koska valitsijaksi katsotaan oikukas demoni, voidaan ajatella, että valinnan tekemiseen annetaan kääntäjälle täysi vapaus: se voi esimerkiksi valita aina ensimmäisen niistä, aina viimeisen niistä tai sitten se voi esimerkiksi heittää arpaa niiden välillä (hui!). Fiksu optimoiva kääntäjä saattaa valita aina sen, joka on kevyin suorittaa. Pääsääntö on siis, että valinnanvapaus annetaan kääntäjälle (ja sen toteuttajalle) ja ohjelmoijan pitää hyväksyä se, että mikä tahansa suorituskelpoisista vaihtoehdoista valitaan. Vahtikomentopohjainen if-lause valitsee vaihtoehdoista demonisesti jonkin suorituskelpoisen ja suorittaa sitä vastaavan lauseen. Jos mikään vaihtoehdoista ei ole suorituskelpoinen, if-lauseen suoritus epäonnistuu. Vahtikomentopohjainen do-silmukka suorittaa vahtikomentonsa toistuvasti kunnes vahtikomento muuttuu suorituskelvottomaksi. Siis silmukan suoritus loppuu, kun kaikki vahtilausekkeet saavat arvokseen false. Esimerkki 1 Seuraava vahtikomentopohjainen if-lause valitsee muuttujien a ja b arvoista pienemmän ja sijoittaa sen muuttujaan min: if a b min a a b min b Esimerkki 2 Seuraavassa ohjelmapätkässä a, b ja c ovat lukutaulukoita ja sekä a että b on järjestetty kasvavaan suuruusjärjestykseen. Ohjelmanpätkä yhdistää a:n ja b:n c:hen niin, että c:ssä on jokainen a:n ja b:n alkio ja c on kasvavassa suuruusjärjestyksessä. Muuttujassa n on taulukon a koko ja muuttujassa m on taulukon b koko. Taulukko c on riittävän iso. 8

var i : unsigned int; j : unsigned int; k : unsigned int; begin i 0; j 0; k 0; do i < n (j m a[i] b[j]) begin c[k] a[i]; i i + 1; k k + 1 end j < m (i n a[i] b[j]) begin c[k] b[j]; j j + 1; k k + 1 end end 2.4 Väitteet Väitteet (engl. assertions) ovat testejä, joiden odotetaan saavan arvokseen aina true. Niiden tarkoituksena on dokumentoida ohjelmoijien tekemiä oletuksia ja osoittaa, milloin näitä oletuksia rikotaan. Ne ovat pääsääntöisesti testauksen apuvälineitä (testioraakkeleita). Yleiskäyttöisin tapa lisätä väitteet kieleen on lisätä siihen assert-lause: κ ::= assert ε Hyödyllinen olisi lohkorakenne pre ε 1 begin κ post ε 2, jossa ε 1 on väitelauseke, jonka tulee olla totta lohkoon tultaessa, ja jossa ε 2 on väitelauseke, jonka tulee olla totta lohkosta poistuttaessa. Väitettä ε 1 sanotaan esiehdoksi (engl. precondition) ja väitettä ε 2 sanotaan jälkiehdoksi (engl. postcondition). Ajatus on, että tämä lohkorakenne ilmaisee sopimuksen: lohkon käyttäjän vastuulla on, että esiehto pätee, ja lohkon itsensä vastuulla on, että jälkiehto pätee. Jos lohkon käyttäjä rikkoo sopimuksen ja esiehto ei pädekään, lohkolla ei ole mitään vastuuta eikä sen tarvitse huolehtia jälkiehdosta. Näin esimerkiksi voidaan kirjoittaa seuraavasti: pre a 0 b > 0 begin 9

q a / b; r a % b; post a = q b + r 0 r r < b end Muita hyödyllisiä väitteitä ovat ns. pysyväisväittämät (engl. invariants). Yleisellä tasolla pysyväisväittämä liittyy aina johonkin muuttujaan tai tyyppiin rajoittaen ko. muuttujan tai tyypin arvoja. Esimerkiksi kalenterityypin pysyväisväittämä saattaa vaatia, että huhtikuussa on enintään 30 päivää. Toisaalta while-ohjelmien todistustekniikoissa merkittävä rooli on silmukkainvariantilla, joka ilmaisee jonkin (yleensä hyvinkin epätriviaalin) suhteen silmukkamuuttujan ja muiden muuttujien välillä. Tony Hoare on kirjoittanut mielenkiintoisia artikkeleita [4, 5] väitteiden käytöstä teoriassa ja käytännössä. 3 Funktiokielten kontrollinohjaus Funktiokielissä ainoa varsinainen kontrollia ohjaava konstruktio on aliohjelmarakenne. Jos kielen aliohjelmat voivat olla väljiä jonkin argumenttinsa suhteen (eli ko. argumentin suorituksen päättyminen virheeseen tai jumiutuminen ei aiheuta vastaavaa ongelmaa aliohjelmalle itselleen), mitään muuta ei tarvita periaatteessa jopa ehtolauseke voidaan rakentaa aliohjelmien avulla muussa tapauksessa lisäksi tarvitaan erillinen tuki ehdolliselle haarautumiselle. Kaikki muut rakenteet ovat merkitysopillisesti kielioppimakeisia (syntactic sugar), ne voidaan palauttaa merkityksen muuttumatta funktion määrittelyihin ja kutsuihin. 4 Logiikkakielten kontrollinohjaus Logiikkaohjelma koostuu jonosta faktoja ja sääntöjä. Fakta on predikaatti, jossa voi esiintyä muuttujia; sääntö on muotoa jos nämä niin tämä. Ohjelma voi myös sisältää kyselyjä, joilla on faktajonon muoto. Logiikkaohjelman kontrollivuo koostuu todistusyrityksistä ja peruutuksesta (backtracking). Suoritus alkaa kyselystä. Kysely koostuu predikaateista, joiden konjunktio pitäisi todistaa. Tämä tapahtuu todistamalla kukin predikaatti erikseen, järjestyksessä. 10

Predikaatti todistetaan etsimällä ohjelmasta sellaista faktaa, jonka muoto (kun muuttujat tulkitaan jokerimerkeiksi) on sama kuin todistettavalla predikaatilla, tai sellaista sääntöä, jonka vasen puolen muoto on sama kuin todistettavalla predikaatilla. Muodon samuus todetaan käyttämällä erityistä samastusalgoritmia (unification). Mikäli löytyy tällainen fakta, predikaatti on todistettu. Mikäli löytyy tällainen sääntö, otetaan muistiin, mitä atomia kukin säännön vasemmalla puolella esiintyvä muuttuja vastaa todistettavassa predikaatissa. Tämäkin kuuluu samastusalgoritmin tehtäviin. Predikaatti on todistettu, mikäli säännön oikea puoli, kun vasemmalla puolella esiintyneet muuttujat korvataan siinä niitä vastaavilla todistettavan predikaatin atomeilla. Jos todistus epäonnistuu, etsitään seuraava sopiva fakta tai sääntö. Mikäli sopivaa faktaa tai sääntöä ei löydy, todetaan, että predikaattia ei voida todistaa. Kontrollin liikettä seuraamalla huomataan, että kukin sääntö (sekä triviaalilla tavalla kukin fakta) on aliohjelman määritelmä, ja kukin todistettava predikaatti on aliohjelman kutsu. Peruutuksella tarkoitetaan sitä tilannetta, jossa säännön oikean puolen todistaminen epäonnistuu. Tällöin kontrolli palaa kyseisestä säännöstä, ja kokeillaan seuraavaa sopivaa faktaa tai sääntöä. 5 Aliohjelmat Aliohjelma (subroutine) on useimpien kielten tärkein kontrollin abstrahointikeino. 5.1 Kutsusekvenssit Aliohjelmaan kontrolli siirtyy sen kutsun (call) kautta. Kun aliohjelman suoritus päättyy, kontrolli siirtyy takaisin sinne, missä kutsu tehtiin toisin sanoen aliohjelman jatkeena on kutsun jatke. Tämä johtaa siihen, että aliohjelmien kutsurakenne on lifo-tyyppinen (last in, first out), ja toteutustekniikaksi sopii mainiosti pino. Konekielitasolla kutsuoperaatio laittaa aktivaatiopinoon (activation stack) itseään seuraavan käskyn osiotteen ja sitten siirtää kontrollin aliohjelmalle (eli hyppää aliohjelman alkuun, suorittaa go to -lauseen, jonka kohteena on aliohjelman alku). Viimeisenä toimenaan aliohjelma ottaa pinosta päällimmäisenä olevan osoitteen ja hyppää tuohon osoitteeseen. Tavallisesti aliohjelmat ovat parametrisoituja. Parametrisoidun aliohjelman kutsu välittää aliohjelmalle dataa, jota aliohjelman odotetaan käyttävän hyväksi. Tätä dataa kutsutaan aliohjelman argumenteiksi (argument) kielitasolla tarkastellen kyse on lausekkeista, joiden arvo annetaan aliohjelmalle. Myös argumentit on tapana antaa aliohjelmalle pinon välityksellä. Konekielitasolla siis kutsuoperaatio 11

laittaa pinoon paluuosoitteen jälkeen viimeisen argumentin, sitten toiseksi viimeisen argumentin, ja lopulta se laittaa pinoon ensimmäisen argumentin. Sitten se suorittaa hypyn. Joissakin koneissa argumentit sijoitetaan pinoon ennen paluuosoitetta, joissakin koneissa ne laitetaan rekistereihin, joissakin myös paluuosoite laitetaan rekisteriin. Tällaiset eroavuudet ovat väistämättömiä. Säännöt siitä, mitä pinossa ja rekistereissä tarkkaan ottaen tulee olla kutsuun liittyvän hypyn hetkellä, sekä siitä, miten kutsun jäljet siivotaan aliohjelman päätyttyä, muodostavat kutsusekvenssin (calling sequence). On tärkeää, että kutsuja ja aliohjelma noudattavat samaa kutsusekvenssiä. Erityisen tarkkana tämän kanssa pitää olla, jos aliohjelma ja kutsu on tuotettu eri kääntäjillä. Tässä monisteessa oletetaan yksinkertaisuuden vuoksi seuraavanlainen kutsusekvenssi: paluuosoite on pinossa alimmaisena ja sitten tulevat argumentit käännetyssä järjestyksessä, aliohjelma poistaa argumentit ja paluuosoitteen pinosta ennen paluuhyppyä. Teemme tähän myöhemmin muutoksia. Tärkeää on nyt huomata, että paluuosoite voidaan ajatella aliohjelmalle annettuna ylimääräisenä, viimeisen argumentin jälkeen tulevana argumenttina. Näin aliohjelmakutsu on tulkittavissa argumentteja välittävänä go to -lauseena [7]! Tämä havainto on tärkeä, sillä kutsun tulkitseminen hypyksi mahdollistaa monia merkittäviä optimointeja. Esimerkiksi jos jokin aliohjelma kutsuu toista aliohjelmaa viimeisenä tekonaan, voidaan tätä yksinkertaistaa reilusti: ensiksi se poistaa itselleen tulleet argumentit (paluuosoitetta lukuunottamatta) pinosta, sitten se laittaa kutsun argumentit (paluuosoitetta lukuunottamatta) pinoon. Pino on nyt sellaisessa tilassa, että näyttäisi kuin tämän aliohjelman kutsuja olisi tekemässä tätä kutsua. Nyt voidaan tehdä hyppy kutsuttavaan aliohjelmaan. Koska pinossa oleva paluuosoite on suoraan kutsuvan aliohjelman kutsujaan, ei hypyn jäljessä tarvitse olla enää yhtään koodia, aliohjelma päättyy tuohon hyppyyn. Myöskin on niin, että kutsuvasta aliohjelmasta ei jää merkkiäkään pinoon, jolloin tällaisia häntäkutsuja (tail call) voidaan tehdä loputtomasti ilman, että pinon koko kasvaisi sen seurauksena rajattomasti. Tätä optimisaatiota sanotaan häntäkutsun poistoksi (tail call elimination). Sillä on myös merkitysopillista merkitystä. Kun häntäkutsu (eli aliohjelman lopuksi tehtävä kutsu) ei kasvata pinon kokoa, sitä voidaan käyttää iteraation ilmaisemiseen. Tunnetuin esimerkki kielestä, jossa iteraatio ilmaistaan häntärekursiona (tail recursion), on Scheme, jonka määrittelydokumentti vaatii jokaiselta Schemetoteutukselta luotettavan häntäkutsun poiston. Schemessä olisi esimerkiksi kertoman laskeminen luonnollista ilmaista näin: (define (! n) (define (loop i accu) 12

(if (eq? i 1) accu (loop (- i 1) (* accu i)))) (loop n 1)) Jotkut aliohjelmat palauttavat (return) tietoa kutsujalle päätyttyään. Tämä tieto laitetaan paluun yhteydessä pinoon paluuosoitteen tilalle (joissakin koneissa se laitetaan rekisteriin). Aliohjelmaa, joka palauttaa tietoa kutsujalle, sanotaan funktioksi (function). Muut aliohjelmat ovat proseduureja (procedure). Joissakin kielissä kaikkii aliohjelmat ovat funktioita, ja paluuarvoton tilanne hoidetaan palauttamalla arvo, jolla ei ole väliä. Joissakin kielissä on erityinen tyyppi tällaista paluuarvoa varten. Funktion määrittelyssä on kieliopillinen pulma. Onko funktion runko lauseke vai lause? Jos se on lauseke, funktion paluuarvo on luonnollisesti tuon lausekkeen arvo, mutta jotta tällainen funktio olisi hyödyllinen, tulee lausekkeisiin 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. 5.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. 13

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 virhealttiimpi) 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 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. 5.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. 14

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). 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 15

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 aliohjelmaa 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). 5.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. Vuorottaisrutiinien tuki on nykykielissä varsin harvinaista, mutta niitä muistuttaa tietorakenteiden läpikäyntiin tarkoitettu generaattoritekniikka (tunnetaan myös nimellä iteraattori), jota tukee mm. Python ja C#. Generaattori on funktio, joka voi palauttaa kontrollin väliaikaisesti kutsujalle (esimerkiksi Pythonissa yield-lauseella) välittäen samalla kutsujalle arvon. 16

5.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. 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ä 1 Onko aliohjelma OO-olio vai OO-olio aliohjelma? Vai ovatko ne jonkin kolmannen käsitteen ilmentymiä? Viitteet [1] Lloyd Allison. A practical introduction to denotational semantics. Number 23 in Cambridge Computer Science Texts. Cambridge University Press, 1986. [2] Edsger W. Dijkstra. Go to statement considered harmful. Communications of the ACM, 11(3), March 1968. Letter to the editor. 17

[3] Edsger W. Dijkstra. A Discipline of Programming. Prentice-Hall, Englewood Cliffs, NJ, 1976. [4] C. A. R. Hoare. Assertions: a personal perspective. IEEE Annals of the History of Computing, 25(2), 2003. [5] Tony Hoare. Towards the verifying compiler. In Olaf Owe, Stein Krogdahl, and Tom Lyche, editors, From Object-Orientation to Formal Methods: Essays in Memory of Ole-Johan Dahl, volume 2635 of Lecture Notes in Computer Science. Springer, 2004. [6] Donald E. Knuth. Structured programming with go to statements. Computing Surveys, 6(4), December 1974. [7] Guy Lewis Steele, Jr. Debunking the expensive procedure call myth or procedure call implementations considered harmful or, LAMBDA: The ultimate GOTO. In James K. Ketchel et al., editors, Proceedings of the 1977 annual conference, pages 153 162. ACM Press, 1977. 18