Opiskelumateriaali - Lauseet ja lausekkeet

Transcription

Opiskelumateriaali - Lauseet ja lausekkeet
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
Lauseet ja lausekkeet imperatiivisissa ohjelmointikielissä
Tässä dokumentissa käsitellään lauseiden ja lausekkeiden muodostamista sekä
kontrollirakenteita imperatiivisissa ohjelmointikielissä. Esimerkeissä esiintyviä kieliä
käsitellään yksityiskohtaisemmin mm. seuraavissa teoksissa: [Ker] (C-kieli), [Strou] (C++
-kieli), [Kor] (Pascal), [Kur] (Ada), [Arc] (C#), [Arn] (Java) ja [KLS] (FORTRAN). Maarit
Harsu käsittelee vastaavia asioita kirjansa [Har] luvuissa 3 ja 5.
1. Lausekkeet
Lausekkeet (expressions) ovat ohjelmointikielen peruselementtejä. Niiden avulla
voidaan ohjata tietokoneen laskentaoperaatioita ohjelmointikielellä. Jokaisessa
ohjelmointikielessä voidaan muodostaa lausekkeita yhdistelemällä operaattoreita ja
operandeja. Lauseke palauttaa aina ohjelmaan jonkin arvon, joka saadaan
evaluoimalla lauseke. Yleensä operaattorit ovat unaarisia (kohdistuvat yhteen
operandiin) tai binäärisiä (kohdistuvat kahteen operandiin). Esimerkiksi operaattori voi olla sekä unaarinen, jolloin -A vaihtaa muuttujan A arvon etumerkin, tai binäärinen,
jolloin A-B palauttaa muuttujien A ja B arvojen erotuksen. Joissakin kielissä käytetään
myös ternäärisiä, eli kolmeen operandiin kohdistuvia operaattoreita. Lausekkeen
evaluoinnin ymmärtäminen on olennainen osa ohjelmointikielen tuntemusta.
Lausekkeen operandit voivat olla vakioita, muuttujia tai funktiokutsuja.
Lausekkeen evaluointi tarkoittaa sen arvon laskemista käyttäen lausekkeessa
esiintyvien vakioiden ja muuttujien suoritushetkisiä arvoja. Aritmeettisten,
matematiikasta peräisin olevien lausekkeiden automaattinen evaluointi oli jo
ensimmäisten korkean tason ohjelmointikielten tavoitteena, onhan esimerkiksi
FORTRAN lyhennys sanoista FORmula TRANslator (tai FORmula TRANslating system,
[KLS] s. 6). Aritmeettisen lausekkeen evaluoinnin toteuttaminen vaatii operandien
arvojen hakemisen ja operaatioiden soveltamisen näihin operandeihin. Tarvitaan
luonnollisesti erilaisia sääntöjä, jotta lausekkeen arvo voidaan määrittää. Erityisesti
pitää päättää operaattoreiden suoritusjärjestys, operaattoreiden assosiatiivisuus ja
operandien arvojen evaluointijärjestys. Jotkin kielet, esimerkiksi C++ ([Strou], luku 11)
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
ja Ada ([Kur], kappale 5.2), sallivat ohjelmoijan uudelleenmääritellä (ylikuormittaa,
overload) operaattoreita, ts. kirjoittaa oman operaationsa korvaamaan kielessä jo
esiintyvä operaattori tai laajentaa sen sovellusaluetta. Java -kieleen ei tätä C++ -kielen
ominaisuutta ole sisällytetty. Huolellisesti käytettynä operaattorin uudelleenmäärittely
lisää koodin luettavuutta, mutta mikäli operaattori uudelleenmääritellään
luonnottomasti, saattaa tulos olla päinvastainen.
Funktion arvon evaluointi imperatiivisissa kielissä käyttää yleensä innokasta
evaluointia (eager evaluation). Tämä tarkoittaa sitä, että funktion arvo evaluoidaan
joka kerran sitä kutsuttaessa käyttämällä syöteargumenttien senhetkisiä arvoja.
Funktionaalisissa kielissä saatetaan käyttää myös toisen tyyppistä periaatetta, laiskaa
evaluointia (lazy evaluation). Funktion sivuvaikutuksiksi (side effects) sanotaan sen
vaikutusta funktion parametreihin tai globaaleihin muuttujiin. Jos funktio muuttaa joko
parametriensa tai globaalien muuttujien arvoja, on funktiolla sivuvaikutuksia. Erityisesti
funktioiden sivuvaikutuksia samoissa lausekkeissa funktiokutsun kanssa esiintyviin
muuttujiin on syytä varoa. Mitä tulostaa esimerkiksi seuraava C++ -ohjelma?
int g_int;
int main()
{
g_int = 0;
int oddVal = f(g_int) + g(g_int);
printf("Loppuarvo = %d g_int = %d\n", oddVal, g_int);
return 0;
}
int f(int y)
{
if(y < 10)
g_int += 11;
else
g_int += 13;
return g_int;
}
int g(int x)
{
if(x < 10)
g_int = 15;
else
g_int = 5;
return g_int;
}
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
Lausekkeen evaluoinnissa käytettävien operaattoreiden suoritusjärjestys määräytyy
kielen presedenssisääntöjen ja assosiaatiosääntöjen mukaan. Presedenssisäännöt
säätelevät operaattoreiden keskinäistä suoritusjärjestystä (suoritetaanko kertolasku
ennen yhteenlaskua jne.). Assosiaatiosäännöillä puolestaan määrätään, kumpi
kahdesta presedenssiltään samanarvoisesta operaattorista suoritetaan ensin. Yleisesti
presedenssisäännöt noudattavat matematiikasta tuttuja lakeja. Assosiaatiosääntönä on
yleisimmin vasemmalta oikealle tapahtuva suorittaminen. Lähes kaikissa
ohjelmointikielissä voidaan sulkumerkinnöillä vaikuttaa ohjelmallisesti
suoritusjärjestykseen.
Kaikissa ohjelmointikielissä on toteutettu aritmeettiset perusoperaattorit yhteenlasku
(+), vähennyslasku (-), kertolasku (*) ja jakolasku (/). Joissakin kielissä on myös
potenssiin korotukselle oma operaattorinsa, esimerkiksi FORTRANissa tämä on **
([KLS], kappale 6.1.4). Pascal -kielessä on myös oma operaattorinsa div kokonaislukujen
jakolaskulle, jonka tuloksena on kokonaisluku ([Kor], kappale 3.2). Yleisesti myös
kokonaislukujen jakolaskussa syntyvälle jakojäännökselle on oma operaattorinsa, C,
C++ ja Java -kielissä tämä on %, ja Pascalissa mod. Unaarisista operaattoreista
mainittakoon vielä C, C++ ja Java -kielissä esiintyvien operaattoreiden ++ ja -- kaksi
muotoa prefix ja postfix. Prefix-muodossa operaattori kirjoitetaan muuttujan eteen ja
sitä sovelletaan muuttujaan ennen lausekkeen muuta evaluointia ja postfix-muodossa
operaattori kirjoitetaan muuttujan jälkeen ja evaluointi tapahtuu vasta muun
evaluoinnin jälkeen. Näin ollen esimerkiksi C-ohjelmassa
int myTestVar = 0,myOtherVar = 1;
myTestVar = myOtherVar++;
muuttujan myTestVar arvoksi tulee 1 ja muuttujan myOtherVar arvoksi 2. Jos
operaattori muutetaan prefix -muotoon
int myTestVar = 0,myOtherVar = 1;
myTestVar = ++myOtherVar;
molempien muuttujien arvo on lauseen suorituksen jälkeen 2. Seuraavaan taulukkoon
on koottu joidenkin kielien operaattoreiden presedenssisääntöjä:
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
C
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
Java/C++
Ada
FORTRAN
Pascal
Korkein postfix ++, --
postfix ++, -prefix ++, --
**, abs
**
*, /, div,
mod
prefix ++, --
unaarinen +,
-
*, /, mod
*, /
(kaikki) +, -
unaarinen +,
-
*, /, %
*, /, %
Alhaisin
unaarinen +, (kaikki) +,
-
binäärinen +, binäärinen +,
-
binäärinen +,
-
Adan abs -operaattori on luvun itseisarvo.
Assosiaatiosäännöt samoille kielille ovat seuraavassa taulukossa:
Kieli
Assosiaatiosääntö
C
Oikealta prefix ++, --, unaarinen +, Vasemmalta muut
C++/Java
Oikealta ++, --, unaarinen +, Vasemmalta muut
Pascal
Vasemmalta kaikki
FORTRAN
Oikealta **
Vasemmalta muut
Ada
Vasemmalta kaikki paitsi **
** on ei-assosiatiivinen
Esimerkiksi potenssiinkorotuksen oikealta assosiatiivisuus tarkoittaa sitä, että
FORTRANissa lauseke 2**3**2 saa arvon 512 (korotetaan ensin 3 potenssiin 2 = 9 ja
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
sitten 2 potenssiin 9). Jos tässä käytettäisiin vasemmalta assosiatiivisuutta, tuloksena
oli 64 (2 potenssiin 3 on 8 ja 8 toiseen on 64).
APL on sikäli erikoinen ohjelmointikieli, että siinä ei ole lainkaan operaattoreiden
presedenssijärjestystä (ts. kaikki operaattorit ovat samanarvoisia) ja lausekkeen arvo
määräytyy täysin assosiatiivisuussäännön nojalla. APL:ssä assosiatiivisuussääntö on
oikealta vasemmalle kaikille operaattoreille.
Joskus aritmeettisten lausekkeiden evaluoinnissa suoritetaan tyyppimuunnoksia, jotka
voivat olla eksplisiittisiä (ohjelmoijan tekemiä) tai implisiittisiä (tapahtuvat
automaattisesti). Aritmeettisia operaatioita voidaan suorittaa eri tietotyypeille,
esimerkiksi kokonaisluku voidaan jakaa reaaliluvulla. Tällaisissa tapauksissa kielessä
täytyy olla selkeät säännöt operaation tuloksen tyypistä ja siitä, minkä tyyppisinä
operaatiossa esiintyviä operandeja käsitellään. Tällöin joudutaan usein tekemään
pakotettuja tyypinmuunnoksia (coercion), koska yleensä binääriset operaatiot on
määritelty vain saman tietotyypin operandeille. Joskus implisiittiset tyypinmuutokset
voivat aiheuttaa yllätyksiä, esimerkiksi C -kielessä
double osamaara;
osamaara = 3/2;
muuttujan osamäärä arvoksi tulee 1.0 eikä 1.5, kuten voisi kuvitella. Sama koskee C++ja Java-kieltä. Tämä johtuu siitä, että kääntäjä tulkitsee literaalit 3 ja 2 kokonaisluvuiksi
ja suorittaa kokonaislukujen jakolaskun, jonka tulos on 1. Tämä sitten muunnetaan
double-tyyppiseksi. Mikäli lauseke muutettaisiin muotoon
osamaara = 3.0/2;
kääntäjä tulkitsisi osoittajan liukuluvuksi, muuntaisi myös nimittäjän liukuluvuksi ja
suorittaisi liukulukujen jakolaskun, jonka tuloksena olisi 1.5. Pascal -kielessä tällaiset
ongelmat on vältetty niin, että jakolasku / tuottaa aina reaaliluvun ja kokonaislukujen
jakolaskulle on oma operaattori div.
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
Useimmissa kielissä myös eksplisiittinen tyypinmuunnos on mahdollinen ja se tapahtuu
erityisellä operaattorilla tai muistuttaa syntaksiltaan funktiokutsua. Esimerkiksi Java kielessä voitaisiin varmistaa jakolaskun asianmukainen lopputulos
int osoittaja = 3;
int nimittaja = 2;
double osamaara = (double)osoittaja/nimittaja;
Myös C ja C++ -kielessä tyypinmuunnos tehdään samoin; C -pohjaisissa kielissä
eksplisiittistä tyypinmuunnosta kutsutaan usein kastaukseksi (cast). C++-kielessä on
myös neljä operaattoria (static_cast, dynamic_cast, reinterpret_cast, const_cast)
eksplisiittistä tyypinmuunnosta varten. Näitä pitäisikin käyttää mieluummin kuin edellä
mainittua tyyliä. Ada -kielessä eksplisiittinen tyypinmuunnos muistuttaa funktiokutsua:
osamaara: FLOAT;
osoittaja, nimittaja: INTEGER;
BEGIN
osoittaja := 3;
nimittaja := 2;
osamaara := FLOAT(osoittaja)/FLOAT(nimittaja);
Eksplisiittinen tyypinmuunnos voi olla joko laajentava tai kaventava. Kaventava
tyyppimuunnos on esimerkiksi muunnos double-tyypistä tyyppiin float Javassa.
Laajentava olisi muunnos päinvastoin. Useimmiten tällaiset tyypinmuunnokset
hyväksytään, joskus kääntäjä voi antaa kaventavasta tyypinmuunnoksesta varoituksen;
kaventavia ja epäsovinnaisia tyypinmuunnoksia kannattaa käyttää harkiten koodissa.
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
Aritmeettisten lausekkeiden lisäksi ohjelmointikielissä voidaan muodostaa
vertailulausekkeita ja loogisia lausekkeita. Vertailulauseke (relational expression)
koostuu kahdesta operandista ja vertailuoperaattorista, joka vertaa operandien arvoja.
Vertailulausekkeen arvo on looginen totuusarvo. Yleisimmissä kielissä käytettävät
vertailuoperaattorit ovat
Operaatio
C, C++, Java Pascal FORTRAN
Yhtäsuuri
==
=
.EQ.
Erisuuri
!=
<>
.NE.
Suurempi
>
>
.GT.
Pienempi
<
<
.LT.
Suurempi tai yhtäsuuri
>=
>=
.GE.
Pienempi tai yhtäsuuri
<=
<=
.LE.
Vertailuoperaattorien presedenssi on kaikissa kielissä alhaisempi kuin aritmeettisten
operaattoreiden, joten aritmeettisten lausekkeiden vertailussa laskutoimitukset
suoritetaan ensin, esimerkiksi jos a = 8 ja b= 4 lausekkeen
a + 5 < 3*b;
arvo on epätosi. Looginen lauseke eli totuusarvolauseke (Boolean expression) koostuu
totuusarvotyypin muuttujista ja vakioista sekä vertailulausekkeista ja totuusarvooperaattoreista (Boolean operators). Loogiselle lausekkeelle lasketaan
propositiologiikan mukaan totuusarvo, joko tosi tai epätosi.
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
Totuusarvo-operaattoreiden presedenssien suhde aritmeettisiin ja
vertailuoperaattoreihin on yleisimmin seuraava:






Looginen negaatio on korkeimmalla presedenssitasolla,
Aritmeettiset operaattorit,
Vertailuoperaattorit,
Looginen XOR,
Looginen JA,
Looginen TAI.
Negaatio-operaattori, looginen JA ja looginen TAI ovat useissa kielissä (esimerkiksi
Pascalissa) NOT, AND ja OR. Java, C ja C++ -kielissä operaattorit ovat !,&& ja ||. Java ja
C++ -kielissä on lisäksi operaattori ^ poissulkevalle taille (exclusive or). Pascal-kieli
poikkeaa yllämainitusta operaattoreiden presedenssijärjestyksestä, sillä Pascalin
vertailuoperaattorit ovat alemmalla tasolla kuin loogiset operaattorit, joista AND on
samalla tasolla kertolaskuoperaattorin kanssa ja OR yhteenlaskuoperaattorin kanssa
([Kor], kappale 4.3). Näin ollen lauseke
a < 10 OR b > 15;
ei Pascalissa käänny, vaan se on esitettävä muodossa
(a < 10) OR (b > 15);
C-kieli on nykyisistä imperatiivisista ohjelmointikielistä sikäli erikoinen, että siitä
puuttuu looginen tietotyyppi ja mikä tahansa numeerinen arvo voidaan tulkita
totuusarvoksi tosi jos se on erisuuri kuin 0 ja totuusarvoksi epätosi jos se on 0. Tämä
johtaa siihen, että kielellä voidaan kirjoittaa varsin kummallisen näköisiä
vertailulausekkeita, esimerkiksi
int x=2,y=1,z=0,u,v;
u = y==z < x;
v = x < y < z;
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
missä muuttujan u arvo sijoituksen jälkeen voi riippua kääntäjästä, mutta on yleisimmin
1. Muuttujan v arvoksi tulee aina 0. Ominaisuus heikentää kielen luettavuutta ja
saattaa vaikuttaa myös luotettavuuteen.
Lausekkeen oikosulkuevaluointi (short-circuit evaluation) tarkoittaa sen arvon
määräämistä ennen kuin sen kaikkien operandien arvoja on määrätty. Esimerkiksi, jos
muuttujalla x on arvo 11, lausekkeen
(x-11) * (z + 1);
ei riipu muuttujan z arvosta. Näin ollen oikosulkuevaluointia käyttämällä voitaisiin
jättää laskematta lausekkeen z+1 arvo. Useissa kielissä oikosulkuevaluointia sovelletaan
loogisiin lausekkeisiin, esimerkiksi - jos oikosulkuevaluointia käytetään - Pascal-kielinen
lauseke (a < 25) AND (b > 10) saa aina totuusarvon epätosi, mikäli a on suurempi tai
yhtäsuuri kuin 25. Tässä tapauksessa voidaan toinen vertailu jättää tekemättä.
Oikosulkuevaluointia voidaan käyttää hyväksi yksinkertaistamaan koodia, esimerkiksi
koodi
a:=10;b:=0;
IF (b<>0) AND (a/b > 2) THEN
BEGIN
writeln ('a = ',a, 'b = ', b, 'a/b =', a/b);
END;
tuottaa ilman oikosulkuevaluointia nollalla jakamisen, mikäli b = 0.
Oikosulkuevaluointia käytettäessä jakolaskua a/b ei suoriteta jos b = 0. Tosin Pascal kielen standardissa ei pakoteta käyttämään oikosulkuevaluointia; useissa toteutuksissa
se kuitenkin on käytössä. Sen sijaan C, C++ ja Java -kielissä käytetään loogisten
lausekkeiden evaluoinnissa aina tätä menetelmää ([Seb], kappale 7.6), samoin C#:ssa.
Aritmeettisten lausekkeiden evaluoinnissa ei yleensä käytetä oikosulkumenetelmää.
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
2. Lauseet
Ohjelman voidaan ajatella koostuvan primitiivisistä eli atomisista lauseista sekä näitä
yhdistelemään tarkoitetusta ohjauslauseista (kontrollilauseista). Yhdessä
peräkkäisyyden (kompositio) kanssa näiden avulla muodostetaan kaikki kielen
ohjausrakenteet. Primitiivisiä lauseita ovat sijoituslause, aliohjelmakutsu ja tyhjä
lause. Ohjelmointikielessä on jotenkin erotettava lauseet toisistaan. Yleisin nykyään
käytetty tapa on käyttää erikoismerkkiä päättämään lause. Pascalissa erikoismerkkiä
käytetään erottamaan lauseet (joten viimeinen lause ei tarvitse mitään erikoissymbolia
loppuunsa) ja FORTRANissa rivinvaihto päättää lauseen samoin kuin Pythonissa. Lisäksi
on joitakin kieliä (esimerkkinä CLU), joissa kielen syntaksi on suunniteltu niin, ettei
lauseiden erotin- tai lopetusmerkkiä tarvita. Aliohjelmakutsuihin palataan myöhemmin,
tyhjää lausetta käytetään tapauksissa, joissa kielen syntaksin takia on oltava lause,
mutta ohjelmoija ei halua mitään toimintoa suoritettavaksi. Tässä käsitellään
tarkemmin sijoituslausetta ja ohjauslauseita.
Sijoituslause (assignment) on imperatiivisen ohjelmoinnin perusta, sijoituslauseella
mallinnetaan von Neumannin koneen muistin manipulointia. Sijoituslauseen avulla
ohjelmoija sitoo dynaamisesti muuttujan tunnisteen tietokoneen muistipaikassa
olevaan arvoon. Useimmiten kielen sijoitusoperaattorina käytetään
yhtäsuuruusmerkkiä =, Pascalissa ja Adassa yhdistelmää :=. Sekaannusten välttämiseksi
kielten vertailuoperaattori eroaa sijoitusoperaattorista. PL/I -kielessä symbolit ovat
samat, mitä ei voi pitää kielen luettavuuden ja luotettavuuden kannalta hyvänä
ratkaisuna. Lisäksi PL/I salli sijoittaa useampaan muuttujaan saman arvon tyyliin
EKA, TOKA = 0
Tämän jälkeen sekä muuttujan EKA että TOKA arvo on 0; tämäkin huonontaa kielen
luettavuutta ja luotettavuutta. C-, C++- ja Java-kielissä voidaan käyttää kahta unaarista
operaattoria sijoituslauseena, nimittäin ++ ja --. Ohjelmassa rivi
luku++; (tai ++luku;)
tarkoittaa sijoitusta
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
luku = luku+1;
Operaattoreista on prefix- ja postfix-muodot, mikä tarkoittaa sitä, että postfix muodossa lisäys tapahtuu vasta lausekkeen evaluoinnin jälkeen, mikäli operaattori
esiintyy lausekkeessa ja prefix-muodossa ennen lausekkeen evaluointia. Siten prefix muodossa
int ekaluku, tokaluku = 3;
ekaluku = --tokaluku;
muuttujan ekaluku arvoksi tulee 2, mutta postfix -versiossa
int ekaluku, tokaluku = 3;
ekaluku = tokaluku--;
muuttujan ekaluku arvoksi tulee 3. Lauseiden
int tokaluku,luku = 3;
tokaluku = luku++ + ++luku;
jälkeen tokaluku saa arvon 8.
Aina ALGOL68:sta lähtien moniin kieliin on sisällytetty yhdistettyjä
sijoitusoperaattoreita, joita käyttäen samaan muuttujaan sijoitettava arvo voidaan
kirjoittaa lyhemmin. Esimerkiksi C, C++ ja Java -kielissä voidaan muuttujalle tehdä
binäärisiä operaatioita seuraavalla tavalla: Lause
summa += luku;
on lyhennysmerkintä lauseelle
summa = summa + luku;
ja lause
osamaara /= luku;
lauseelle
osamaara = osamaara/luku;
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
C, C++, C# ja Java sallivat ehdollisten lausekkeiden käyttämisen. Ehdollinen lauseke on
muotoa
<boolean_expression> ? <expression1> : <expression2>
Lauseke saa lausekkeen <expression1> arvon, mikäli <boolean_expression> on tosi,
muussa tapauksessa lausekkeen <expression2> arvon. Javassa ja C#:ssa lauseke ei toimi
itsenäisenä lausekkeena, vaan sitä on käytettävä sijoituslauseessa, ts.
z = x == 0 ? (y=1) : (y=100);
on sallittu kaikissa neljässä kielessä, mutta
x == 0 ? (y=1) : (y=100);
ainoastaan C:ssä ja C++:ssa.
Ainakin C-, C++-, C#- ja Java-kielissä sijoituslause on myös lauseke, ts. se palauttaa
ohjelmaan arvon, joka on sama kuin kohdemuuttujaan sijoitettu arvo. Tästä syystä
sijoituslause voi esiintyä myös operandina lausekkeessa, esimerkiksi
int x,y,z = 5;
x = y = z;
sijoittaa muuttujan z arvon sekä muuttujaan x että y. Tämä mahdollistaa myös melko
kompaktin koodin kirjoittamisen, esimerkiksi C-kielinen lause
while( *a++ = *b++);
kopioi merkkijonon b jonoon a. Kielen luettavuutta tällainen ohjelmointityyli ei
ainakaan lisää. Yleensäkin sijoituslauseen käyttäminen operandina lisää lausekkeisiin
sivuvaikutuksia, jotka heikentävät kielen luettavuutta. Luotettavuuskin heikkenee,
etenkin C-kielessä, jossa ei tunneta loogista tietotyyppiä. Näin ollen esimerkiksi
ehdollinen toiminto
if(a == b)
{
jne
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
muuttuu helposti kirjoitusvirheen seurauksena muotoon
if(a = b)
{
jne
jolloin ohjelman merkitys muuttuu täysin, mutta on kuitenkin hyväksyttävää koodia.
Ohjauslauseilla (control statements) toteutetaan kielen ohjausrakenteet.
Periaatteessa ohjelmointikielessä tultaisiin toimeen hyvin vähillä ohjausrakenteilla:
Muuttujien arvojen yhtäsuuruuden testaaminen ja while-toistorakenne riittävät minkä
tahansa yleensä kirjoitettavissa olevan ohjelman kirjoittamiseen. Ohjelmoijan työn
helpottamiseksi kuitenkin useimmissa kielissä on huomattavasti rikkaampi
ohjauslauseiden joukko. Yleisesti käytetyt ohjausrakenteet ovat muotoutuneet 1960- ja
1970- lukujen taitteessa syntyneen rakenteisen ohjelmoinnin (structured
programming) kehittymisen myötä. Tällöin nimittäin havaittiin, että ohjelmoinnin
tarpeisiin sinänsä riittävät peräkkäisyys ja ehdollinen hyppykäsky johtivat suuremmissa
ohjelmissa helposti lukukelvottomaan ja epäluotettavaan koodiin. Rakenteisen
ohjelmoinnin ihanteiden mukaan ohjelma koostuu sisäkkäisistä rakenteista, joilla
kullakin on yksi tulo- ja poistumiskohta. Tämä helpottaa koodin ymmärtämistä ja
virheiden etsimistä mikäli sellaisia esiintyy. Jotta erilaiset rakenteet voidaan luontevasti
toteuttaa, on ohjelmointikielessä syytä olla useammanlaisia valinta- ja toistolauseita.
Useissa nykykielissä (esimerkiksi Java) hyppykäsky (goto) on jätetty kokonaan pois.
Ohjausrakenteiden käytön helpottamiseksi on useimmissa kielissä mahdollista koota
joukko (peräkkäisiä) lauseita yhdeksi kokonaisuudeksi. Tällaista kokonaisuutta
kutsutaan yhdistetyksi lauseeksi (compound statement) mikäli sen sisällä ei sallita
esitellä uusia muuttujia, tai lohkoksi (block) jos uusien muuttujien esittely sallitaan.
Yhdistetyt lauseet esiintyivät ensimmäiseksi ALGOL 60:ssä, FORTRAN-kielessä tällaista
mahdollisuutta ei alkujaan ollut. Pascalissa voidaan rakentaa yhdistettyjä lauseita
mutta ei lohkoja, sen sijaan C-pohjaiset kielet sallivat lohkot. Yleinen piirre nykykielten
ohjausrakenteissa on, että niiden säätelemän koodin suoritus alkaa aina ensimmäisestä
lauseesta: katsotaan että useamman tulokohdan sallimisen riskit (luettavuuden
heikkeneminen, monimutkaisuuden lisääntyminen) ovat huomattavasti suuremmat,
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
kuin se joustavuusetu mikä tällä saavutettaisiin. Sen sijaan ohjausrakenteen sisältä
voidaan yleisesti poistua useammasta kohdasta.
Valintalauseet (selection statements) voidaan jakaa kahden vaihtoehdon lauseisiin ja
monivalintalauseisiin. Tähän väliin sijoittuu FORTRANin ensimmäisissä versioissa
toteutettu aritmeettinen valintalause (ks. [KLS] kappale 13.1), joka oli muotoa
IF (ARITMEETTINEN LAUSEKE) N1, N2, N3
Tässä N1, N2 ja N3 ovat viitteitä, joiden osoittamiin kohtiin hypätään lausekkeen arvon
perusteella: Jos lauseke saa positiivisen arvon, hypätään kohtaan N1, nollalla kohtaan
N2 ja negatiivisella arvolla kohtaan N3. Yleensä lausetta käytetään muodossa:
IF
10
…
GO
20
…
GO
30
…
40
(C) 10, 20, 30
…
TO 40
…
TO 40
…
Kuitenkin hyppykohdat voivat olla missä tahansa ohjelmassa, mikä on suuri ongelma
ohjelman luettavuuden kannalta ja siten käytettynä myös altistaa virheille. Sittemmin
FORTRANissa siirryttiin käyttämään yhden vaihtoehdon valintalausetta
IF (LOOGINEN LAUSEKE) LAUSE
missä LAUSE oli jokin yksittäinen ohjelmalause (yleensä hyppykäsky) eikä esimerkiksi
toinen IF -lause. Myöhemmin FORTRAN -kielessä valintalausetta on edelleen
modernisoitu, FORTAN 77:ssä voidaan käyttää niin sanottua lohko-IF -rakennetta, jonka
muoto on
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
IF (EHTO) THEN ...
[ELSE IF (EHTO2) THEN
...
ELSE IF (EHTO3) THEN
...
...
ELSE
...]
END IF
Useimpien nykykielten valintalause pohjautuu ALGOL 60 -kielen kahden vaihtoehdon
valintalauseeseen:
if (<looginen_lauseke>) then <lause1> [else <lause2>]
Käyttämällä yhdistettyjä lauseita näin saadaan ohjelmoitua joustavia valintarakenteita.
Useimmissa kielissä on myös sama syntaksiongelma, joka johtuu siitä, että if-lauseella
ei ole loppulekseemiä. Koska else -osa on vapaaehtoinen, syntyy ns. roikkuvan elsen
ongelma: sisäkkäisistä if - else -rakenteista ei voida varmuudella sanoa, mihin if lauseeseen else kuuluu. Esimerkiksi C -kielessä (jossa if lauseessa samoin kuin C++:ssa
ja Javassa then -sanasta ei käytetä)
if(x < 0)
if (y > 10)
x = 10;
else
x = 20;
Lauseen syntaksista sinänsä ei voida päätellä, onko sijoitus x = 20 tarkoitus tehdä, jos
(x>= 0) vai jos (x < 0 ja y <= 10). Ongelma on ratkaistu määrittelemällä kielten
semantiikka niin, että else liittyy aina lähimpään if -lauseeseen. Näin ollen ylläolevassa
koodissa sijoitus x= 20 tehdään, jos (x < 0 ja y < = 10). Tämä ongelma altistaa
tahattomille virheille: ongelma voidaan välttää käyttämällä aina yhdistettyjä lauseita if rakenteissa. Pascal -kielessä on vastaava ongelma, mutta Pascal -kieleen pohjautuvasta
Adasta ongelma on poistunut, koska Adassa if -lause päätetään end if -merkinnällä (ks.
[Kur], kappale 3.5)). Adan valintalause on muotoa:
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
if ehto then
lause;
lause;
...
[else
lause;
lause;
...
]
end if;
Näin ollen Adassa esimerkkikoodi olisi kirjoitettava joko
if(x < 0) then
if (y > 10) then
x := 10;
end if;
else
x := 20;
end if;
tai
if(x < 0) then
if (y > 10) then
x := 10;
else
x := 20;
end if;
end if;
ja kummassakin tapauksessa voidaan päätellä, kumpaan if -lauseeseen else -osa
kuuluu.
Yleisimmin nykyään käytetty monivalintarakenne pohjautuu alkuaan ALGOL-W -kieleen
sisältyvään case -lauseeseen. Alkuperäinen muoto oli C.A.R. Hoaren esittämä
case <kokonaisluku_lauseke> of
begin
<lause_1>;
<lause_2>;
...
<lause_n>
end
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
Lausekkeen arvon perusteella valitaan suoritettava lause, arvo 1 valitsee lauseen
lause_1 jne. Suoritettavat lauseet voivat olla yhdistettyjä lauseita. Samankaltainen
valintarakenne on käytössä useissa yleisissä kielissä - valintalausekkeena saa yleensä
olla mikä tahansa lueteltavaa tyyppiä oleva lauseke. Pascal -kielen case -lause on
muotoa
case lauseke of
const_list_1: <lause_1>;
const_list_2: <lause_2>;
...
const_list_n: <lause_n>
end
missä lauseke on lueteltavaa tyyppiä ja lauseet voivat olla yhdistettyjä lauseita. Mikäli
lausekkeen arvo on sama kuin jokin listoissa luetelluista vakioarvoista const_list_1,
const_list_2 jne., vastaava lause suoritetaan ja tämän jälkeen kontrolli siirtyy case lauseen jälkeiseen lauseeseen. Alkujaan Pascalissa ei lainkaan määritelty, mitä aiheutuu
siitä, että lausekkeen arvo ei ole mikään luetelluista arvoista. Useimmat Pascal toteutukset sallivat oletustoiminnon käyttämällä else -rakennetta, ts.
case lauseke of
const_list_1: <lause_1>;
const_list_2: <lause_2>;
...
const_list_n: <lause_n>
else <oletus_lause>
end
jolloin oletus_lause suoritetaan, mikäli lausekkeen arvoa ei löydy luetelluista arvoista.
C-pohjaisissa kielissä monivalintalauseen muoto on
switch(<lauseke>) {
case vakio_1: <lause_1>;
case vakio_2: <lause_2>;
…
case vakio_n: <lause_n>;
[default: <oletus_lause>]
}
Myös tässä tapauksessa lauseke saa olla mitä tahansa lueteltavaa tyyppiä. Lauseen
semantiikka on seuraava: lausekkeen arvoa verrataan lueteltuihin vakioihin ja kun arvo
löydetään, suoritetaan listan lauseita tästä lauseesta alkaen, kunnes kohdataan break -
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
lause tai kontrollirakenne loppuu. Kummassakin tapauksessa kontrolli siirtyy
rakennetta seuraavaan lauseeseen. Tällainen break-lauseen käyttö lisää joustavuutta
varsinkin, koska case-osissa ei voi luetella arvoja. Toisaalta se altistaa myös
ohjelmointivirheille, joita voi olla vaikea havaita testauksessa. Jostakin case-osasta
breakin unohtaminen voi johtaa ohjelmassa harvoin esiintyviin virhetilanteisiin.
Tietokoneen todellinen voima on sen kyvyssä toistaa nopeasti operaatioita. Sen vuoksi
toisto on tietokoneohjelmoinnin oleellisimpia rakenteita. Toisto voi perustua
iteraatioon tai rekursioon; iteraatio on jonkin lausejoukon useamman kertaista
toistamista, kun taas rekursio on aliohjelman sisäkkäistä kutsumista. Imperatiivisissa
kielissä iteratiivinen toisto on yleisempää ja tehokkaampaa kuin rekursiivinen; toki
myös rekursio on mahdollinen useimmissa imperatiivisissa kielissä. Funktionaalisessa
ohjelmoinnissa taas rekursio on luonnollisempi toiston muoto. Tässä tutustutaan
imperatiivisten kielten iteratiivisiin toistorakenteisiin. Toistorakenteet voidaan jakaa,
hieman väljästi, määrättyihin ja määräämättömiin toistolauseisiin. Määrätyssä
toistolauseessa jokin laskuri kontrolloi toistoa ja toistojen lukumäärä tiedetään
etukäteen; määräämättömässä toistolauseessa puolestaan jokin looginen ehto
kontrolloi toistoa ja toistojen lukumäärää ei välttämättä tiedetä ennalta.
Toistorakenteen kontrolloimaa ohjelmalohkoa sanotaan usein silmukaksi (loop).
Määrätyissä toistolauseissa silmukkalaskuri (silmukkamuuttuja ,loop variable)
kontrolloi silmukan päättymistä. Silmukkamuuttujan alkuarvo (initial value), loppuarvo
(terminal value) ja askel (stepsize) ovat silmukkaparametreja (loop parameters),
joiden arvot on silmukkaan tultaessa jotenkin asetettava. Määrättyä
silmukkarakennetta suunniteltaessa on otettava monia seikkoja huomioon:




Mikä on silmukkamuuttujan tyyppi ja näkyvyysalue?
Mikä on silmukkamuuttujan arvo (jos yleensä on olemassa) silmukan
päättymisen jälkeen?
Saako silmukkamuuttujan arvoa muuttaa silmukan sisällä ja vaikuttaako muutos
silmukan suorituksen lopettamiseen?
Evaluoidaanko silmukkaparametrit ainoastaan kerran vai joka iteraatiokerralla?
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
FORTRANin varhemmissa versioissa toistorakenteen muoto oli
DO <viite> <muuttuja> = <alkuarvo>,<loppuarvo> [,<askel>]
Tässä silmukan rajaa viite, ts. esimerkiksi luvut 1,3,5,7 ja 9 tulostava silmukka voitaisiin
kirjoittaa
DO 100 I = 1,10,2
PRINT *,I
100 CONTINUE
FORTRANissa yleensä aina kirjoitetaan silmukan viimeiseksi käskyksi CONTINUE, joka ei
tee mitään operaatiota. FORTRANin versiossa 77 silmukkamuuttuja sai olla
kokonaisluku- tai reaalilukutyyppiä, mutta kielen versiossa 90 silmukkamuuttujat
rajattiin kokonaislukutyyppisiksi. Muutenkin tässä versiossa muutettiin DO-silmukan
rakennetta hieman. Tässä tarkoitetaan varhaisempien versioiden toistorakenteita
FORTRANista puhuttaessa. FORTRANissa ei saa muuttaa silmukkamuuttujan arvoa
silmukan sisällä ja silmukkamuuttujan arvoksi jää sille viimeksi määritelty arvo.
FORTRANissa silmukkaparametrit evaluoidaan kerran silmukkaan tultaessa ja näitä
käyttäen lasketaan silmukan kierrosluku. FORTRANissa siis aina määrätään etukäteen
silmukan suorituskerrat.
ALGOL 60:ssä pyrittiin joustavuuteen for -silmukkaa määriteltäessä, tässä kielessä
silmukan rakenne on seuraava:
<for_stmt> ::= for var := <list_elemt> {, <list_elemt>} do <statement>
<list_elemt> ::= <expression>
| <expression> step <expression> until <expression>
| <expression> while <Boolean_expr>
ALGOL 60 siis yhdistää loogisen ja laskuriin perustuvan toistorakenteen. Tämä johtaa
kuitenkin siihen, että on mahdollista kirjoittaa oikeaoppisia, mutta hyvin monimutkaisia
silmukkaehtoja. Tällöin kielen luettavuus heikkenee. Esimerkiksi seuraavat ovat
mahdollisia toistorakenteita ALGOL 60:ssä
for laskuri := 1,3,5,7 do
summa := summa + laskuri;
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
for laskuri := 1 step 2 until 7 do
summa := summa + laskuri;
Nämä ovat selkeitä ja niissä laskuri käy samat arvot läpi, mutta yhdistelemällä sallitusti
ehtoja, voidaan kirjoittaa silmukka
for laskuri := 1,15,9 step 2 until 17,
2 * laskuri while laskuri < 150,
-5, 22 do
summa := summa + laskuri;
mitä ei voida pitää selkeän koodin malliesimerkkinä, koodia joutuu tutkimaan hetken,
ennen kuin selviää, että laskuri käy läpi arvot 1,15,9,11,13,15,17,34,68,136,-5,22.
Lisäksi ALGOLin kaikki silmukkaparametrit evaluoidaan jokaisella silmukan
suorituskerralla: näin saataisiin vielä vaikeammin hallittavia silmukoita muuttamalla
esimerkiksi askelta ja ylärajaa silmukan sisällä.
Pascal -kielen for -lause on sen sijaan yksinkertainen:
for var := alkuarvo (to | downto) loppuarvo do <statement>
Askel on aina kooltaan 1 ja se tehdään joko ylös- tai alaspäin. Silmukkamuuttujan on
oltava lueteltavaa tyyppiä; muuttuja on normaali muuttuja, jonka näkyvyysalue
määräytyy kuten muuttujien yleensäkin. Silmukkamuuttujan arvo silmukan päättyessä
on epämääräinen. Alkuarvon ja loppuarvon evaluointi tapahtuu ainoastaan kerran,
joten näiden muuttaminen (mikäli riippuvat muuttujista) silmukan sisällä ei vaikuta
toistokertojen määrään. Silmukkalaskurin arvoa ei saisi muuttaa silmukan sisällä,
yleensä Pascalissa ei kuitenkaan tarkisteta tätä ja voi olla mahdollista vaikuttaa
silmukan suoritukseen muuttamalla laskurin arvoa kesken silmukan. Tämä ei
kuitenkaan ole suositeltavaa, koska tulokset saattavat olla erilaisia eri järjestelmissä.
C-pohjaisten kielten for -lause on erittäin joustava, se on nimittäin muotoa
for(lauseke_1;lauseke_2;lauseke_3)
lause;
Tässä yhteydessä varsinaisesta silmukkamuuttujasta ei voida puhua. C-kielessä lause
toimii seuraavasti: Lauseen ensimmäinen lauseke suoritetaan ainoastaan kerran, ennen
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
silmukan ensimmäistä suorittamista. Toinen lauseke kontrolloi silmukan päättymistä;
lauseke_2 evaluoidaan ennen jokaista silmukan kierrosta ja mikäli tuloksena on nollasta
poikkeava arvo, silmukan seuraava kierros suoritetaan. Huomaa, että lauseke voi olla
useiden lausekkeiden muodostama lista (ks. [Ker], kappale 3.5), jolloin sen arvo on
viimeisen lausekkeen arvo. Siis
for(i = 12; x = 0, y = 1; i++)
{
printf("LUUPPI\n");
}
määrittelee C-kielessä ikuisen silmukan. Kolmas lauseke (lauseke_3) evaluoidaan
jokaisen kierroksen jälkeen. C++ -kielen for -lause eroaa C -kielen lauseesta kahdella
tavalla: C++:ssa voidaan käyttää toisena lausekkeena myös loogisia lausekkeita (mikä ei
varhaisemmassa C-kielessä onnistu, koska C ei tunne loogista tietotyyppiä) ja C++kielessä ja nykyään myös C-kielessä voidaan lisäksi esitellä muuttujia lausekkeissa. Näin
ollen
for(int i = 0; i < 12; i++)
{
// SILMUKKA
}
on sallittu C++ -kielessä, mutta ei C-kielessä ennen sen versiota C99. Javan for -lause on
muuten samanlainen kuin C++ -kielen, mutta Javassa toisen lausekkeen on oltava
looginen lauseke. Lisäksi Java ei salli lausekelistaa toisena lausekkeena kuten C++. Siis
bool bb = true, ba = false;
for(int i = 0; bb = true, ba = false ; i++)
{
// SILMUKKA
}
kelpaa C++ :ssa mutta
boolean bb = true, ba = false;
for(int i = 0; bb = true, ba = false ; i++)
{
// SILMUKKA
}
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
ei käy Javassa. C# noudattaa samaa käytäntöä kuin Java. Kaikissa mainituissa Cpohjaisissa kielissä for -silmukan lausekkeet voivat olla tyhjiä, esimerkiksi
for(;;)
{
// SILMUKKA
}
määrittelee ikuisen silmukan, koska tyhjänä toinen lauseke katsotaan todeksi. Ctyyppisessä for-lauseessa kaikkia silmukkaparametreja voidaan muuttaa silmukan
sisällä. Kannattaa muistaa, että kolmas lauseke suoritetaan joka kerran silmukan
suorittamisen jälkeen, joten esimerkiksi silmukan
for(int i = 0; i <= 12; i++)
{
// SILMUKKA
}
jälkeen muuttujan i arvo on 13. C-kielessä, kuten myös C++ -kielessä, voidaan tehdä
hyppykäsky keskelle silmukkaa, millä voi kuitenkin olla omituisia seurauksia, joten näin
ei pitäisi koskaan menetellä.
Python-kielen for-silmukka on muotoa
for <target> in <object>:
<lauseet>
[else:
<lauseet>]
missä object on jonkinlaisen iteroitava olio, jonka arvoja muuttuja target saa yksi
kerrallaan. Vapaaehtoisen else-osan lauseet suoritetaan silmukan lopuksi, ellei
silmukassa suoriteta break-lausetta.
Määräämättömissä toistolauseissa lopetusehtona toimii jokin looginen ehto, eikä
toistokertoja välttämättä tiedetä ennakolta; useimmissa kielissä on toteutettu erilaisia
rakenteita tällaisille toistoille. Itse asiassa ALGOL 60- kielen toistolausekin on tällainen
rakenne. Sama koskee C-pohjaisia kieliä, vaikka C-kielen for-lauseen päätarkoitus
saattaa ollakin laskurisilmukkana toimiminen. Määräämättömät toistolauseet ovat
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
rakenteeltaan yksinkertaisempia, joten niiden problematiikka on helpompi kuin
toistolauseen. Tällaisen rakenteen suunnittelussa huomioon pitää ottaa lähinnä
seuraavat kysymykset:


Testataanko ehto ennen silmukan suorittamista (alkuehtoinen toisto, pretest) vai
sen jälkeen (loppuehtoinen toisto, posttest)?
Onko määräämätön toistolause määrätyn toistolauseen erikoistapaus vai
erillinen lause?
ALGOL 60:ssä kaikki toistorakenteet toteutetaan samalla for -lauseella, joka on
rakenteeltaan monimuotoinen ja -mutkainen. FORTRANissa ei ole lainkaan loogista
toistolausetta, toistolauseet on toteutettava DO -silmukkaa ja hyppykäskyjä käyttäen.
Java, C, C++ ja C# sisältävät toistolauseet sekä alku- että loppuehtoiselle toistolle.
Alkuehtoinen muoto on
while(lauseke)
lause;
ja loppuehtoinen
do
lause;
while(lauseke);
Loppuehtoisen toiston lause suoritetaan ainakin kerran, vaikka lauseke olisikin aluksi
epätosi. (C:ssä lauseke on jokin aritmeettinen lauseke tai lausekkeiden muodostama
lista, Javassa lausekkeen on oltava looginen lauseke eikä listaa sallita).
Myös Pascal -kielessä on sekä alkuehtoinen että loppuehtoinen toisto mahdollinen;
Pascalissa alkuehtoinen toisto on
WHILE ehto DO
lause;
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
ja loppuehtoinen
REPEAT
Lause1;
Lause2;
…
UNTIL ehto;
Pascalissa loppuehtoisen toiston lopetuslogiikka on käänteinen C-kieleen verrattuna, ts.
lauseita toistetaan, kunnes ehto tulee todeksi. Toinen erikoinen seikka tässä
toistorakenteessa on, että silmukassa sallitaan peräkkäisiä lauseita (ei tarvitse käyttää
koottua lausetta), kun tämä yleensä ei ole mahdollista Pascalin muissa rakenteissa.
Tämä heijastaa ortogonaalisuuden puutetta kielen suunnittelussa.
Joissakin tapauksissa on ohjelmoijan kannalta mukavaa sallia toistorakenteesta
poistuminen itse valitusta kohdasta. Monet kielet sallivat tällaisen toteutuksen.
Esimerkiksi Ada -kielessä on rakenne silmukalle, joka on ikuinen, ellei ohjelmoija
määrittele poistumiskohtaa seuraavasti
loop
…
…
if(toistoja > 10) then exit;
end if;
…
…
end loop;
Tällöin silmukasta poistutaan exit -lauseen määräämästä kohdasta heti, kun annettu
ehto toteutuu. Myös C-kieleen pohjautuvissa kielissä silmukasta voidaan poistua
pakottavasti - tämän tekee break -käsky. Näin ollen yllä olevaa Ada -koodia vastaava C kielinen koodi olisi
for(;;)
{
…
…
if(toistoja > 10) break;
…
…
}
Ari Vesanen, Tietojenkäsittelytieteiden laitos, Oulun yliopisto
815338A Ohjelmointikielten periaatteet: Lauseet imperatiivisissa kielissä
Tässä for(;;) voitaisiin myös korvata lauseella while(1) (tai while(true) Javassa). Samassa
silmukassa voi olla useita poistumiskohtia. Kovin monien poistumiskohtien käyttäminen
haittaa kuitenkin koodin luettavuutta, joten niitä kannattaa käyttää harkiten.
Rakenteisen ohjelmoinnin ihanteisiin kuuluu periaate, jonka mukaan jokaisella
rakenteella on yksi tulo- ja poistumiskohta; tätä periaatetta voidaan rikkoa
kirjoittamalla useita poistumiskohtia silmukkaan. Monien ohjelmointiongelmien
kannalta on kuitenkin kätevää säilyttää myös pakotettu poistuminen silmukasta, joten
useimmissa kielissä kyseinen ominaisuus on toteutettu. Pythonissa on, kuten sen forsilmukassakin while-rakenteessa vapaaehtoinen else-osa, joka suoritetaan silmukan
lopuksi, ellei silmukasta poistuta break-lauseella.
Lähteet
[Arc] Archer, Tom. Inside C#. Edita, IT Press, 2001.
[Arn] Arnold, Ken Gosling, James. The Java Programming Language, Second Edition,
Addison-Wesley 1998
[Har] Harsu, Maarit. Ohjelmointikielet, Periaatteet, käsitteet,
valintaperusteet, Talentum 2005.
[Ker] Kernighan, Richie. The C Programming Language. Prentice Hall 1988.
[KLS] Kortela, Larmela, Salmela. FORTRAN 77. OtaData 1985.
[Kor] Kortela, Larmela, Planman. Pascal-ohjelmointikieli. OtaData 1980.
[Kur] Kurki-Suonio Reino. Ada-kieli ja ohjelmointikielten yleiset perusteet. MODEEMI ry
Tampere 1983.
[Seb] Sebesta, Robert W. Concepts of Programming Languages 10th edition, Pearson
2013.
[Strou] Stroustrup, Bjarne. The C++ Programming Language, 3rd edition, Murray Hill
1997.