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.
© Copyright 2024