250 likes | 353 Views
11. Javan toistorakenteet. Sisällys. Laskuri- ja lippumuuttujat. Sisäkkäiset silmukat. Tyypillisiä ohjelmointivirheitä: Silmukan rajat asetettu kierroksen verran väärin. Ikuinen silmukka. Silmukoinnin lopettaminen break -lauseella. Yleistä.
E N D
Sisällys • Laskuri- ja lippumuuttujat. • Sisäkkäiset silmukat. • Tyypillisiä ohjelmointivirheitä: • Silmukan rajat asetettu kierroksen verran väärin. • Ikuinen silmukka. • Silmukoinnin lopettaminen break-lauseella.
Yleistä • Toisto ilmaistaan while-, do-while- tai for-lauseella. • Lauseet toimivat lähes kuten pseudokoodi-vastineensa - ehtolausekkeet on vain esitettävä Javan merkintätavoin. • Toistorakenteita sanotaan tuttavallisemmin silmukoiksi (loop). • goto varattu sana vaikka goto-rakenne ei ole Javassa käytettävissä.
Silmukan toistoa ohjaava ehtolause muotoillaan usein laskurimuuttujan (counter) avulla. Tyypillisesti laskurilla on kokonaislukuarvo, joka kasvaa yhdellä kullakin kierroksella. Voidaan nimetä lyhyesti. Esim. int i = 0; while (i < 10) { i = i + 1; ... } Laskurin arvoa voidaan päivittää myös tehtävään paremmin sopivin tavoin. Esim. int i = 10; while (i > 0) { i = i - 1; ... } Esim. int i = 1; while (i < 7) { i = i + 2; ... } Laskurimuuttujat
Lippumuuttujat • Silmukan suoritusta voidaan ohjata myös niin sanotun lippumuuttujan (flag) avulla. • Hyödyllinen apuväline, kun toistojen määrää ei ole kiinnitetty eikä laskurimuuttujasta siten ole hyötyä. • Käytetään while- ja do-while-lauseissa. • Esimerkiksi tekstipohjaisten ohjelmien käyttöliittymässä on yleensä “pääsilmukka”, josta poistutaan vasta, kun käyttäjä antaa ohjelmalle lopetuskomennon jotain näppäintä painamalla.
Lippumuuttujat • Esim. char valinta; boolean jatketaan = true; do { // Suoritetaan lauseita ja luetaan käyttäjän // komento valinta-muuttujaan. … // Asetetaan uusi arvo lippumuuttajalle, kun lopetetaan. if (valinta == LOPETA) jatketaan = false; } while (jatketaan); • Pääsilmukoita on tehty jo aikaisemmin ja niihin palataan, kun opitaan kuinka syötteitä luetaan Javassa.
while (ehto) { lause 1; ... lause m; } Esiehto: lauseita suoritetaan niin kauan kuin ehto on tosi. Aaltosulkeet voi jättää pois, jos m = 1. do { lause 1; ... lause n; } while (ehto); Jälkiehto: lauseet suoritetaan ainakin kerran. Aaltosulkeita ei voi jättää pois, jos n = 1. while- ja do-while-lauseet
Heippa1-algoritmi Java-ohjelmana public class Heippa1 { public static void main(String[] args) { // Muuttujat laskurille ja kierrosten lukumäärälle. int i = 0; int lkm = 3; // Esiehtoinen toisto, jossa tervehditään lkm kertaa. while (i < lkm) { System.out.println("Heippa!"); i = i + 1; } } }
Heippa2-algoritmi Java-ohjelmana public class Heippa2 { public static void main(String[] args) { // Muuttujat laskurille ja kierrosten lukumäärälle. int i = 0; int lkm = 3; // Jälkiehdon vuoksi tervehdittäisiin aina vähintään kerran // ellei do-while lausetta olisi sijoitettu if-lauseen sisään. if (lkm > 0) // Tervehditään lkm kertaa. do { System.out.println("Heippa!"); i = i + 1; } while (i < lkm); } }
for-lause • for (laskurin alustus; ehto; laskurin päivitys) { lause 1; ... lause n; } • Ensin: Alustetaan laskuri ja tarkistetaan ehto. • Sitten: Jos ehto oli tosi, niin suoritetaan lauseet ja päivitetään laskuria, kunnes ehto on epätosi. • Huom! Jos n = 1, aaltosulkeet voidaan jättää pois.
Heippa3-algoritmi Java-ohjelmana public class Heippa3 { public static void main(String[] args) { // Muuttujat laskurille ja kierrosten lukumäärälle. int i; int lkm = 3; // Tervehditään lkm kertaa. for (i = 0; i < lkm; i = i + 1) System.out.println("Heippa!"); } }
Sisäkkäiset silmukat • Joissakin tehtävissä - kuten arvojen lajittelussa ja taulukoiden käsittelyssä - toistorakenteita yhdistetään siten, että toistorakenne sisältyy toiseen toistorakenteeseen. • Tällainen rakenne tunnetaan lyhyesti sisäkkäisinä silmukoina (nested loops). • Silmukoita voi olla sisäkkäin kaksi tai useampia.
Sisäkkäiset silmukat • Tällä kurssilla rajoitutaan pääasiassa yksin-kertaisimpaan sisäkkäiseen toistorakenteeseen, jossa on yksi ulompi ja yksi sisempi silmukka. • Jokaisella ulomman silmukan kierroksella suoritetaan kaikki sisemmän silmukan kierrokset. • Olkoon ulommassa silmukassa m kierrosta ja sisemmässä silmukassa n kierrosta. Tällöin sisempään silmukkaan liittyvät lauseet suoritetaan m ∙ n kertaa.
Sisäkkäiset silmukat public class SisakkaisetSilmukat { public static void main(String[] args) { // Vakiot rivien ja sarakkeiden lukumäärille sekä tulostusmerkille. finalint RIVEJA=3; final int SARAKKEITA=7; final char MERKKI='*'; // Rivi- ja sarakelaskurit. int rivi; int sarake; // Tulostus kahden silmukan avulla. for (rivi = 0; rivi < RIVEJA; rivi = rivi + 1) { for (sarake = 0; sarake < SARAKKEITA; sarake = sarake + 1) System.out.print(MERKKI); System.out.println(); } } }
Peräkkäiset ja sisäkkäiset silmukat ovat eri asia. Peräkkäiset silmukat ovat toisistaan “riippumattomia”, kun taas ulompien silmukoiden suoritus ohjaa sisempien silmukoiden suoritusta. Peräkkäisiä silmukoita: for (i = 0; i < LKM; i = i+1) { ... } ... while (j > LKM) { ... } ... Sisäkkäisiä silmukoita: for (i = 0; i < A; i = i+1) for (j = 0; j < B; j = j+1) { ... } ... Sisäkkäiset silmukat
Joskus ulompi silmukka voi sisältää useampia peräkkäisiä silmukoita, jolloin nämä silmukat suoritetaan kukin vuorollaan aina kullakin ulomman silmukan kierroksella. // Pääsilmukka. do { ... // 1. sisempi silmukka. for (i = 0; i < A; i = i + 1) { ... } ... // 2. sisempi silmukka. while (j > B) { ... } ... } while (valinta != LOPETUS); Sisäkkäiset silmukat
Tyypillisiä ohjelmointivirheitä • Yleisin silmukoihin liittyvä ohjelmointivirhe on se, että toistojen todellinen lukumäärä poikkeaa tavalla tai toisella ajatellusta lukumäärästä. • Tällaisen loogisen virheen seuraus voi olla • lievä, jolloin silmukan viimeinen kierros jää usein suorittamatta tai silmukkaa suoritetaan yhden ylimääräisen kerran tai • vakava, jolloin ohjelma joutuu ikuiseen silmukkaan (eternal loop).
Kierroksen liian lyhyt tai pitkä silmukka • Jos silmukka jää kierroksen “lyhyeksi” tai menee kierroksen “pitkäksi”, on virhe yleensä laskurin alkuarvossa tai silmukan lopetusehdossa. • Tarkastellaan tyypillistä tilannetta, jossa laskuri on • kokonaislukutyyppinen, • laskurin alkuarvo on nolla (tai yksi) ja • laskurille annetaan arvoksi peräkkäisiä kokonaislukuja.
Kierroksen liian lyhyt tai pitkä silmukka • Esim. Suoritetaan silmukkaa viisi kertaa käyttäen laskurina i-muuttujaa. Tällöin i:n arvot voivat olla joko 0, 1, 2, 3 ja 4 tai 1, 2, 3, 4 ja 5. • Kurssimateriaalissa käytetään yleensä nollasta alkavaa laskentaa. Omissa ratkaisuissa alkuarvona voi käyttää joko nollaa tai ykköstä, koska tämä on makuasia. • On kuitenkin muistettava, että alkuarvo on huomioitava silmukan lopetusehdossa.
Kierroksen liian lyhyt tai pitkä silmukka • Jos on esimerkiksi tavoitteena suorittaa n kierrosta alkuarvoa yksi käyttäen, ei lopetusehdoksi käy arvolle nolla sovitettu ehto, koska tällöin suoritetaan vain n - 1 kierrosta. • Esim. int i = 1; int n = 3; // Suoritetaan 2 kierrosta 3:n asemasta. while (i < n) { i = i + 1; ... }
Kierroksen liian lyhyt tai pitkä silmukka • Esim. Olkoon kierroksia n ja i laskurimuuttuja. // n kierrosta. for (i = 0; i < n; i = i + 1) ... // n - 1 kierrosta. for (i = 1; i < n; i = i + 1) ... // n kierrosta. for (i = 1; i <= n; i = i + 1) ... // n + 1 kierrosta for (i = 0; i <= n; i = i + 1) ...
Ikuinen silmukka • Silmukka, jonka suoritus ei pääty koskaan virheellisen logiikan vuoksi. • Kun Java-ohjelma joutuu ikuiseen silmukkaa, se voidaan pysäyttää CTRL-C-näppäinyhdistelmällä tai sulkemalla komentoikkuna. • Esim. while (true) ; • Esim. int i; for (i = 10; i > 0; i = i + 1) System.out.println(i);
break-lause • Keskeyttää (sisimmän) silmukan suorituksen. • Käytetään aina ehdollisena, koska silmukka pysähtyy välittömästi. • Käyttöä syytä välttää, koska break-lause • tekee koodista vaikeaselkoista ja • on korvattavissa lippumuuttujalla. • continue-lause pysäyttää kierroksen, mutta silmukan suoritusta jatketaan, mikäli kierroksia on jäljellä.
break-lause public class Break1 { public static void main(String[] args) { int summa = 0; int i = 0; while (i < 300) { summa = summa + i; // Ei suostuta kasvattamaan summaa paljon yli tuhannen. if (summa > 1000) { System.out.println(summa); // 1038 break; } i = i + 1; } } }
break-lause public class Break2 { public static void main(String[] args) { int i = 0; // Lopetetaan ikuinen silmukka break-lauseella. do { i = i + 1; if (i > 100) break; } while (1 < 2); } }