Ohjelmoinnin peruskäsitteet sekä valinta- ja toistolauseiden toteutus
Ohjelmoinnin peruskäsitteet
Ohjelmoinnissa voidaan ajatella olevan muutama peruskäsite, jotka pitää jollain tavoin toteuttaa. Varsinainen laskentatyö on pääasia ja se oikeastaan tarkoittaa yksinkertaisten laskutoimitusten tekoa. Aritmeettis-loogisten lausekkeiden toteutus konekielellä on hyvin suoraviivaista, koska yleisimmin käytetyt aritmeettis-loogiset operaatiot voidaan toteuttaa suoraan niitä vastaavilla konekäskyillä. Pienenä ongelma-alueena tässä on, että kaikki laskenta tapahtuu rekistereiden avulla ja pienen rekisterijoukon optimaalinen käyttö suuren datajoukon laskentaan ei ole ihan helppoa.
; lausekkeen x = 2x+y toteutus konekielellä
load r1, x
mul r1, =2
add r1, y
store r1, x
Ohjelman kontrolli määrittelee, mistä seuraava konekäsky löytyy nyt suorituksessa olevan jälkeen. Oletusarvoisestihan seuraava konekäsky löytyy edellisen jälkeen muistista, mutta usein kontrolli haarautuu muualle ohjelmalogiikan mukaisesti. Tähän liittyy erilaisten koodin suorituspolkujen valinta ja eri tavoin tapahtuva saman koodin suorituksen toisto.
Aliohjelmat (funktiot, metodit) ovat oma kategoriansa kontrollin siirron suhteen. Niissä on helposti käytettävää parametrisoitua koodia, jota voidaan käyttää kutsumalla mistä tahansa. Aliohjelmat voivat olla kyseisen ohjelmiston omien sisäisten rutiinien lisäksi ohjelmointikielen tai käyttöjärjestelmän palvelurutiineja. Nämä käsitellään seuraavassa luvussa 6.
Käsiteltävä tieto voi olla yksittäisiä sanoja, mutta usein tarvitaan laajempia tietorakenteita. Yksinkertaisissa tietorakenteissa olevaan tietoon pystytään viittaamaan suoraan konekäskystä sopivan tiedonosoitusmoodin avulla. Monimutkaisessa tietorakenteet ovat sellaisia, joissa viitattavan tiedon osoite pitää laskea usean konekäskyn avulla ennen kuin dataviite voidaan tehdä.
Valintalauseet korkean tason kielissä
Tyypillisesti kaikissa korkean tason ohjelmointikielissä on ehdollinen "if ... then ... else ..." kontrollirakenne, jonka avulla valitaan kumpi mahdollisista koodinpätkistä suoritetaan. Tästä on myös yksinkertaisempi "if ... then" muoto, jossa then-haaran koodi suoritetaan vain, jos annettu ehto on voimassa. Sen jälkeen suoritus jatkuu normaalisti "if ... then" lauseen jälkeisessä koodissa joka tapauksessa.
Joissakin ohjelmointikielissä on myös ns. switch tai case lause, jolla mahdollinen suorituspolku valitaan useamman vaihtoehdon väliltä. Esimerkiksi C-kielen switch-lauseella voidaan suoritettava koodinpätkä valita sen mukaan, mikä jonkin lausekkeen arvo tällä hetkellä on. Lisäksi mukana on oletusarvo vaihtoehto, joka valitaan silloin kun mikään erikseen nimetty vaihtoehto ei toteutunut.
switch(error-number) {
case 1: ... ; break;
case 2: ... ; break;
case 8: ... ; break;
default: ...
}
Valintalauseet konekielessä
Konekielessä valintalauseet toteutetaan yksinkertaisesti ehdollisilla hyppykäskyillä.
load r1, X
jnzer r1, xnzer ; jos r1 != 0, hyppää kohtaan xnzer
... ; then-haara
jump done
xnzer ... ; else-haara
done nop
Monivalinta tehdään tyypillisesti vain vertailemalla lausekkeen arvoa eri vaihtoehtoihin
load r1, X
comp r1, =1
jnequ not1
... ; vaihtoehto X=1
jump done
not1 comp r1, =2
jnequ not2
... ; vaihtoehto X=2
jump done
not2 comp r1, =3
jnequ not3
... ; vaihtoehto X=3
jump done
not3 ... ; oletus vaihtoehto (default)
done nop
Tämä on tietenkin aika hidasta, kun keskimäärin pitää puolet vaihtoehdoista käydä läpi ennen oikean löytämistä. Oikean vaihtoedon löytämistä voi nopeuttaa, jos laittaa todennäköisimmät vaihtoehdot ensin, mutta tämä ei useinkaan ole mahdollista. Hyvin kätevä korkean tason kielen rakenne muuttuu siis aika kömpelöksi konekieliseksi toteutukseksi.
Joissakin tapauksissa monivalinta voidaan toteuttaa ns. hyppytaulun (jump table) avulla. Hyppytaulussa on talletettuna eri vaihtoehtojen hyppyosoitteet, joista yksi valitaan haarautumisehdon perusteella. Hyppytaulun huonona puolena on, että sen pitää kattaa kaikki ehtolausekkeen mahdolliset arvot.
JumpT ds 10 ; oleta arvoalue 0-9
; --- eri vaihtoehtoihin liittyvät koodit
case1 ... ; vaihtoehto X=1
jump done
case2 ... ; vaihtoehto X=2
jump done
case8 ... ; vaihtoehto X=8
jump done
caseD ... ; oletus vaihtoehto (default)
jump done
; --- hyppytaulun JumpT alustus
... ; alusta kaikkiin arvo caseD
load r1, =case1 ; muuta vaihtoon 1 liittyvä osoite
load r2, =1
store r1, JumpT(r2)
load r1, =case2
load r2, =2
store r1, JumpT(r2)
load r1, =case8
load r2, =8
store r1, JumpT(r2)
; --- halutun vaihtoehdon valinta
; tässä pitäisi ehkä tarkistaa, onko X:n arvo ok!
load r1, X
load r2, Jump(r1) ; oikean vaihtoehdon osoite
jump 0(r2)
done nop
Toinen vaihtoehto on, että hyppytaulussa onkin eri vaihtoehtoihin johtavat hyppykäskyt, joista yksi valitaan suoritukseen haarautumisehdon perusteella. Taulun alustus ja vaihtoehdon valinta tapahtuvat nyt tietenkin vähän eri tavalla.
; --- halutun vaihtoehdon valinta
load r1, X
jump r2, JumpT(r1) ; hyppää oikeaan vaihtoehtoon hyppäävään käskyyn
done nop
Toistolauseet korkean tason kielissä
Korkean tason kielissä on tyypillisesti muutama erityyppinen toistolause. Toistolauseiden tyyppejä on useanlaisia, koska joihinkin tapauksiin ongelman ratkaisu on helpompaa tietyn tyyppisellä toistolauseella kuin jollakin toisella.
For-silmukassa muuntelumuuttujalle annetaan alkuarvo, sen muutoksen ilmaisema lauseke ja silmukan lopetusehto. Joissakin kielissä sallitaan myös usean muuntelumuuttujan käyttö.
for (i=0; i++; i<30) { /* i++ on sama kuin i=i+1, C tai Java */
A[i] = 4*i;
}
Useimmissa kielissä ehtolauseke tarkistetaan ennen silmukan koodin suorittamista ja tällöin on mahdollista, että silmukan koodia (runkoa) ei suoriteta lainkaan. Joissakin kielissä ehtolauseke tarkistetaan vasta silmukan koodin suorittamisen jälkeen. Tuolloin silmukan koodi suoritetaan aina vähintään yhden kerran.
do 50 i = 0, 30, 1 /* Fortran */
A[i] = 4*i;
50 continue
Toinen tyypillinen toistolause on "while-do" silmukka, jossa silmukkaa suoritetaan kunnes jokin ehto tulee voimaan. Muuntelumuuttujia voi tällöin olla silmukassa useitakin, mutta ne täytyy alustaa while-lauseen ulkopuolella.
i=0; j= 50;
while (i<j) do { /* C tai Java */
x = T[i];
T[i] = T[j];
T[j] = x;
i++; j++;
}
Tästäkin on muoto, jossa silmukan runko suoritetaan ainakin kerran. Siitä käytetään esimerkiksi nimiä "do-while", "do-until" tai "repeat-until" silmukka, ohjelmointikielestä riippuen.
i=0; j= 50;
do { /* C Sharp */
x = T[i];
T[i] = T[j];
T[j] = x;
i++; j++;
}
until i>j;
Eri ohjelmointikielissä on vielä paljon muitakin toistolauseen muotoja, mutta yleensä ne ovat näiden neljän muunnoksia. Saman näköisilläkin toistolauseilla voi olla eri ohjelmointikielissä erilainen merkitys (semantiikka). Joissakin ohjelmointikielissä silmukan rungosta tai koko silmukasta voi poistua esim. exit-lauseella. Joissakin ohjelmointikielissä voi silmukan yhteydessä määritellä uusia muuttujia, jotka ovat olemassa ja viitattavissa vain tuon silmukan sisällä. Emme käsittele näitä silmukoiden erityispiirteitä tässä sen enempää.
Toistolauseet konekielessä
Konekielessä toistolauseita on vain kahden tyyppisiä. Molemmissa alustetaan ensin mahdolliset muuntelumuuttujat. Ensimmäisessä tapauksessa tarkistetaan heti, josko silmukasta poistutaan tällä hetkellä. Jos ei poistuta, niin silmukan runko suoritetaan ja sen jälkeen tehdään mahdolliset muutokset muuntelumuuttujiin. Toisessa vaihtoehdossa silmukan runko ja muutokset muuuntelumuuttujiin suoritetaan ensin ja sitten vasta testataan silmukan päättymisehtoa. Näillä kahdella vaihtoehdolla voidaan toteuttaa kaikki korkean tason kielten toistolauseet. Totta kai varsinaisessa konekielisessä toteutuksessa pitää huomioida kunkin korkean tason kielen semanttiset erityispiirteet.
Esimerkiksi, edellä oleva C-kielen taulukon alustus for-silmukalla voitaisiin toteuttaa konekielellä seuraavasti:
load r1, =0 ; muuntelumuuttujan i alustus, arvo r1:ssä
loop comp r1, =30 ; silmukan lopetustesti
jequ done
load r2, r1 ; silmukan runko
mul r2, =4
store r2, A(r1)
add r1, =1 ; lisätään muuntelumuuttujan i arvoa yhdellä, arvo r1:ssä
jump loop
done nop ; poistu silmukasta
Vastaavasti edellä oleva Fortran-kielinen do-silmukka ("do 50") olisi konekielellä:
load r1, =0 ; muuntelumuuttujan i alustus, arvo r1:ssä
loop load r2, r1 ; silmukan runko
mul r2, =4
store r2, A(r1)
add r1, =1 ; lisätään muuntelumuuttujan i arvoa yhdellä, arvo r1:ssä
comp r1, =30 ; silmukan lopetustesti
jless loop
Vaikka silmukat näyttävät kovin samanlaisilta, niissä on merkittävä semanttinen ero. Ei ole ollenkaan yhdentekevää, testataanko silmukan lopetusehto ennen ensimmäistä silmukan rungon suorituskertaa vai ei.
Muistathan tarkistaa pistetilanteesi materiaalin oikeassa alareunassa olevasta pallosta!