Ehtolauseet ja vaihtoehtoinen toiminta
Ohjelmamme ovat tähän mennessä olleet lineaarisia eli ohjelmien suoritus on tapahtunut ylhäältä alaspäin ilman suuria yllätyksiä tai vaihtoehtoja. Ohjelmiin halutaan kuitenkin usein vaihtoehtoista toiminnallisuutta, eli toiminnallisuutta joka riippuu tavalla tai toisella ohjelmassa olevien muuttujien tilasta.
Jotta ohjelman suoritus voisi haarautua esimerkiksi käyttäjän antaman syötteen perusteella, tarvitsemme käyttöömme ehtolauseen. Yksinkertaisin ehtolause on seuraavanlainen.
System.out.println("Hei maailma!");
if (true) {
System.out.println("Et voi välttää tätä koodia!");
}
Hei maailma! Et voi välttää tätä koodia!
Ehtolause alkaa avainsanalla if
, jota seuraa sulut. Sulkujen sisälle asetetaan lauseke, joka evaluoidaan kun ehtolause saavutetaan. Evaluoinnin tulos on totuusarvo, yllä evaluointia ei tehty, vaan ehtolauseessa käytettiin suoraan totuusarvoa.
Sulkuja seuraa lohko, joka määritellään avaavan aaltosulun {
ja sulkevan aaltosulun }
sisään. Lohkon sisällä oleva lähdekoodi mikäli sulkujen sisälle asetettu lauseke evaluoidaan todeksi (true).
Tarkastellaan esimerkkiä, missä ehtolauseen lausekkeessa vertaillaan lukuja.
int luku = 11;
if (luku > 10) {
System.out.println("Luku oli suurempi kuin 10");
}
Jos ehtolauseen lauseke evaluoidaan todeksi, yllä "jos muuttujassa luku oleva arvo on suurempi kuin 10", ohjelman suoritus siirtyy ehtolauseen määrittelemään lohkoon. Jos taas lauseke on epätotta, ohjelman suoritus siirtyy ehtolauseeseen liittyvän lohkon päättävän aaltosulun jälkeiseen lauseeseen.
Huomaa, että if
-lauseen perään ei tule puolipistettä, sillä lause ei lopu ehto-osan jälkeen.
Ohjelmakoodin sisennyksestä ja lohkoista
Lohkolla tarkoitetaan aaltosulkujen rajaamaa aluetta. Ohjelman sisältävä lähdekooditiedosto sisältää merkkijonon public class
, jota seuraa ohjelman nimi ja lohkon avaava aaltosulku. Lohko päättyy sulkevaan aaltosulkuun. Alla olevassa kuvassa on näytettynä värjättynä ohjelman lohko.
Ohjelmissa toistuva rimpsu public static void main(String[] args)
aloittaa oman lohkon, jonka sisällä oleva lähdekoodi suoritetaan kun ohjelma käynnistetään — rimpsu on oikeastaan jokaisen ohjelman aloituskohta. Yllä olevassa esimerkissä on todellisuudessa kaksi lohkoa, kuten alla olevasta kuvasta huomaamme.
Lohkot määrittelevät ohjelman rakennetta ja rajaavat ohjelmaa. Aaltosuluille tulee aina löytyä pari: koodi, josta josta puuttuu lohkon päättävä (tai aloittava) aaltosulku, on virheellinen.
Myös ehtolause aloittaa lohkon.
Lohkoihin liittyy ohjelman rakenteen ja toiminnan määrittelyn lisäksi luettavuuteen liittyvä seikka. Lohkojen sisällä oleva koodi sisennetään. Esimerkiksi ehtolauseeseen liittyvän lohkon sisältämä lähdekoodi sisennetään neljä välilyöntiä sisemmälle kuin ehtolauseen aloittava if
-komento. Neljä merkkiä saa myös tabulaattorimerkillä (q:n vasemmalla puolella oleva näppäin). Kun lohko sulkeutuu, eli tulee }
-merkki, sisennys loppuu. }
-merkki on samalla tasolla kuin ehtolauseen aloittanut if
-komento.
Alla oleva esimerkki on sisennetty väärin.
if (luku > 10) {
luku = 9;
}
Alla oleva esimerkki on sisennetty oikein.
if (luku > 10) {
luku = 9;
}
Vertailuoperaattorit
Vertailuoperaattoreita ovat seuraavat:
>
suurempi kuin>=
suurempi tai yhtä suuri kuin<
pienempi kuin<=
pienempi tai yhtä suuri kuin==
yhtä suuri kuin!=
erisuuri kuin
int luku = 55;
if (luku != 0) {
System.out.println("Luku oli erisuuri kuin 0");
}
if (luku >= 1000) {
System.out.println("Luku oli vähintään 1000");
}
Luku oli erisuuri kuin 0
Muulloin eli else
Jos ehtolauseen sulkujen sisällä oleva lauseke evaluoituu epätodeksi, ohjelmakoodin suoritus siirtyy ehtolauseen lohkon lopettavan aaltosulun seuraavaan lauseeseen. Tämä ei aina ole toivottua, vaan usein halutaan luoda vaihtoehtoinen toiminta tilanteeseen, missä ehtolauseen lauseke on epätotta.
Tämä onnistuu if
-komennon yhteydessä käytettävän else
-komennon avulla.
int luku = 4;
if (luku > 5) {
System.out.println("Lukusi on suurempi kuin viisi!");
} else {
System.out.println("Lukusi on viisi tai alle!");
}
Lukusi on viisi tai alle!
Jos ehtolauseeseen on määritelty else
-haara, suoritetaan else-haaran määrittelemä lohko jos ehtolauseen ehto ei ole totta. Komento else
tulee samalle riville if
-komennon määrittelemän lohkon lopettavan aaltosulun kanssa.
Lisää vaihtoehtoja: else if
Jos vaihtoehtoja on useampia käytetään else if
-komentoa. Komento else if
on kuin else
, mutta lisäehdolla. else if
tulee if
-ehdon jälkeen, ja niitä voi olla useita.
int luku = 3;
if (luku == 1) {
System.out.println("Luku on yksi");
} else if (luku == 2) {
System.out.println("Lukuna on kaksi");
} else if (luku == 3) {
System.out.println("Kolme lienee lukuna!");
} else {
System.out.println("Jotain muuta!");
}
Kolme lienee lukuna!
Luetaan yllä oleva esimerkki: 'Jos luku on yksi, tulosta "Luku on yksi", muuten jos luku on kaksi, tulosta "Lukuna on kaksi", muuten jos lukuna on kolme, tulosta "Kolme lienee lukuna!". Muulloin, tulosta "Jotain muuta!"'.
Yllä olevan ohjelman askeleittainen visualisointi:
Vertailujen suoritusjärjestys
Vertailut suoritetaan järjestyksessä ylhäältä alaspäin. Kun suorituksessa päästään ehtolauseeseen, jonka ehto on totta, suoritetaan lohko ja lopetetaan vertailu.
int luku = 5;
if (luku == 0) {
System.out.println("Luku on nolla.");
} else if (luku > 0) {
System.out.println("Luku on suurempi kuin nolla.");
} else if (luku > 2) {
System.out.println("Luku on suurempi kuin kaksi.");
} else {
System.out.println("Luku on pienempi kuin nolla.");
}
Luku on suurempi kuin nolla.
Yllä oleva esimerkki tulostaa merkkijonon "Luku on suurempi kuin nolla." vaikka myös ehto luku > 2
on totta. Vertailu lopetetaan ensimmäiseen valintakäskyyn, jonka ehto on totta.
Ehtolauseen lauseke ja totuusarvomuuttuja
Ehtolauseen sulkuihin asetettavan arvon tulee olla lausekkeen evaluoinnin jälkeen totuusarvotyyppinen. Totuusarvomuuttujan tyyppi on boolean
ja arvo true tai false.
boolean onkoTotta = true;
System.out.println("Totuusarvomuuttujan arvo on " + onkoTotta);
Totuusarvomuuttujan arvo on true
Ehtolauseen voi suorittaa myös seuraavasti:
boolean onkoTotta = true;
if (onkoTotta) {
System.out.println("Aika vinhaa!");
}
Aika vinhaa!
Vertailuoperaattoreita voi käyttää myös ehtojen ulkopuolella. Tällöin vertailun tuloksena saatu totuusarvo asetetaan talteen totuusarvomuuttujaan myöhempää käyttöä varten.
int eka = 1;
int toka = 3;
boolean onkoSuurempi = eka > toka;
Yllä olevassa esimerkissä totuusarvomuuttuja onkoSuurempi
sisältää nyt totuusarvon false. Yllä olevaa esimerkkiä voi myös jatkaa ja ottaa siihen mukaan ehtolauseen.
int eka = 1;
int toka = 3;
boolean onkoPienempi = eka < toka;
if (onkoPienempi) {
System.out.println("1 on pienempi kuin 3!");
}
Yllä olevassa kuvassa ohjelmakoodia on suoritettu niin pitkään, että ohjelman muuttujat on luotu ja niihin on asetettu arvot. Muuttujassa onkoPienempi
on arvona true
. Seuraavana suoritetaan vertailu if (onkoPienempi)
— muuttujaan onkoPienempi
liittyvä arvo löytyy sen lokerosta, ja lopulta ohjelma tulostaa:
1 on pienempi kuin 3!
Ehtolauseet ja merkkijonojen vertailu
Siinä missä kokonaislukujen, liukulukujen, ja totuusarvojen samuutta voi verrata kahdella yhtäsuuruusmerkillä (muuttuja1 == muuttuja2
), ei merkkijonojen samuuden vertailu kahdella yhtäsuuruusmerkillä onnistu.
Voit kokeilla tätä seuraavalla ohjelmalla:
Scanner lukija = new Scanner(System.in);
System.out.println("Syötä ensimmäinen merkkijono");
String eka = lukija.nextLine();
System.out.println("Syötä toinen merkkijono");
String toka = lukija.nextLine();
if (eka == toka) {
System.out.println("Merkkijonot olivat samat!");
} else {
System.out.println("Merkkijonot olivat eri!");
}
Syötä ensimmäinen merkkijono sama Syötä toinen merkkijono sama Merkkijonot olivat eri!
Syötä ensimmäinen merkkijono sama Syötä toinen merkkijono eri Merkkijonot olivat eri!
Tämä liittyy merkkijonojen sisäiseen toimintaan sekä siihen, miten muuttujien vertailu on Javassa toteutettu. Käytännössä vertailun toimintaan vaikuttaa se, kuinka paljon tietoa muuttuja voi sisältää — merkkijonot voivat sisältää äärettömän määrän merkkejä, kun taas kokonaisluvut, liukuluvut ja totuusarvot sisältävät aina yhden luvun tai arvon. Muuttujia, jotka sisältävät aina vain yhden luvun tai arvon voi verrata yhtäsuuruusmerkillä, kun taas enemmän tietoa sisältävillä muuttujille tällainen vertailu ei toimi. Palaamme tähän tarkemmin myöhemmin tällä kurssilla.
Merkkijonojen vertailussa käytetään merkkijonomuuttujiin liittyvää equals
-komentoa. Komento toimii seuraavalla tavalla:
Scanner lukija = new Scanner(System.in);
System.out.println("Syötä merkkijono");
String syote = lukija.nextLine();
if (syote.equals("merkkijono")) {
System.out.println("Luit ohjeet oikein, hyvä!");
} else {
System.out.println("Metsään meni!");
}
Syötä merkkijono ok! Metsään meni!
Syötä merkkijono merkkijono Luit ohjeet oikein, hyvä!
Komento equals kirjoitetaan merkkijonomuuttujan jälkeen siten, että se kiinnitetään pisteellä vertailtavaan muuttujaan. Komennolle annetaan parametrina merkkijono, johon muuttujaa vertaillaan. Mikäli merkkijonomuuttujaa vertaillaan suoraan merkkijonoon, voi merkkijonon asettaa hipsuilla merkittynä equals-komennon sulkujen sisään. Muulloin sulkujen sisään asetetaan sen merkkijonomuuttujan nimi, johon merkkijonomuuttujan sisältämää merkkijonoa verrataan.
Alla olevassa esimerkissä luetaan käyttäjältä kaksi merkkijonoa. Ensin tarkastetaan ovatko syötetyt merkkijonot samat, jonka jälkeen tarkastetaan onko syötettyjen merkkijonojen arvo "kaksi merkkijonoa".
Scanner lukija = new Scanner(System.in);
System.out.println("Syötä kaksi merkkijonoa");
String eka = lukija.nextLine();
String toka = lukija.nextLine();
if (eka.equals(toka)) {
System.out.println("Merkkijonot olivat samat!");
} else {
System.out.println("Merkkijonot olivat eri!");
}
if (eka.equals("kaksi merkkijonoa")) {
System.out.println("Nokkelaa!");
}
if (toka.equals("kaksi merkkijonoa")) {
System.out.println("Ovelaa!");
}
Syötä kaksi merkkijonoa hei maailma Merkkijonot olivat eri!
Syötä kaksi merkkijonoa kaksi merkkijonoa maailma Merkkijonot olivat eri! Nokkelaa!
Syötä kaksi merkkijonoa samat samat Merkkijonot olivat samat!
Loogiset operaatiot
Ehtolauseen lauseke voi koostua useammasta osasta, joissa käytetään loogisia operaatioita ja &&
, tai ||
, sekä ei !
.
- Kahdesta lausekkeesta koostuva lauseke, joka yhdistetään ja-operaatiolla, on totta jos ja vain jos yhdistettävistä lausekkeista molemmat evaluoituvat todeksi.
- Kahdesta lausekkeesta koostuva lauseke, joka yhdistetään tai-operaatiolla, on totta jos jompikumpi tai molemmat yhdistettävistä lausekkeista evaluoituvat todeksi.
- Loogista operaatiota ei käytetään totuusarvon muuntamiseen truesta falseksi tai falsesta trueksi.
Seuraavassa yhdistetään &&
:lla eli ja-operaatiolla kaksi yksittäistä ehtoa. Koodilla tarkistetaan, onko muuttujassa oleva luku suurempi kuin 4 ja pienempi kuin 11, eli siis välillä 5-10:
System.out.println("Onkohan luku väliltä 5-10: ");
int luku = 7;
if (luku >= 5 && luku <= 10) {
System.out.println("On! :)");
} else {
System.out.println("Ei ollut :(");
}
Onkohan luku väliltä 5-10: On! :)
Seuraavassa annetaan ||
:n eli tai-operaation avulla kaksi vaihtoehtoa, onko luku pienempi kuin 0 tai suurempi kuin 100. Ehto toteutuu jos luku täyttää jommankumman ehdon:
System.out.println("Onkohan luku pienempi kuin 0 tai suurempi kuin 100");
int luku = 145;
if (luku < 0 || luku > 100) {
System.out.println("On! :)");
} else {
System.out.println("Ei ollut :(");
}
Onkohan luku pienempi kuin 0 tai suurempi kuin 100 On! :)
Seuraavassa käännetään !
ei-operaatiolla lausekkeen luku > 4
tulos. Ei-operaatio merkitään lauseketta ennen niin, että käännettävä lauseke rajataan suluilla, ja ei-operaatio lisätään sulkuja ennen.
int luku = 7;
if (!(luku > 4)) {
System.out.println("Luku ei ole suurempi kuin 4.");
} else {
System.out.println("Luku on suurempi tai yhtäsuuri kuin 4.");
}
Luku on suurempi tai yhtäsuuri kuin 4.
Alla on kuvattuna lausekkeiden toimintaa kun lausekkeissa on loogisia operaatioita.
luku | luku > 0 | luku < 10 | luku > 0 && luku < 10 | !(luku > 0 && luku < 10) | luku > 0 || luku < 10 |
---|---|---|---|---|---|
-1 | false | true | false | true | true |
0 | false | true | false | true | true |
1 | true | true | true | false | true |
9 | true | true | true | false | true |
10 | true | false | false | true | true |
Ehtolauseiden suoritusjärjestys
Tutustutaan ehtolauseiden suoritusjärjestykseen klassisen ohjelmointiongelman kautta.
'Kirjoita ohjelma, joka kysyy käyttäjältä lukua yhden ja sadan väliltä ja tulostaa luvun. Jos luku on kolmella jaollinen, luvun sijaan tulostetaan "Fizz". Jos luku on viidellä jaollinen, luvun sijaan tulostetaan "Buzz". Jos luku on sekä kolmella että viidellä jaollinen, luvun sijaan tulostetaan "FizzBuzz"'.
Ohjelmoija lähtee ratkaisemaan tehtävää lukemalla ongelmakuvauksen, ja luomalla ohjelmakoodia ongelmakuvausta seuraten. Koska ohjelman suoritusehdot esitellään ongelmassa annetussa järjestyksessä, muodostuu ohjelman rakenne järjestyksen perusteella. Ohjelman rakenne muodostuu seuraavien askelten perusteella:
- Tee ohjelma, joka lukee luvun käyttäjältä ja tulostaa sen.
- Jos luku on jaollinen kolmella, tulosta luvun sijaan merkkijono "Fizz".
- Jos luku on jaollinen viidellä, tulosta luvun sijaan merkkijono "Buzz".
- Jos luku on jaollinen kolmella ja viidellä, tulosta luvun sijan merkkijono "FizzBuzz".
Jos-tyyppiset ehdot on helppo toteuttaa if - else if - else
-valintakäskyjen avulla. Alla oleva koodi on toteutettu yllä olevien askelten perusteella, mutta se ei kuitenkaan toimi oikein, kuten alla olevista esimerkeistä huomataan.
Scanner lukija = new Scanner(System.in);
int luku = Integer.valueOf(lukija.nextLine());
if (luku % 3 == 0) {
System.out.println("Fizz");
} else if (luku % 5 == 0) {
System.out.println("Buzz");
} else if (luku % 3 == 0 && luku % 5 == 0) {
System.out.println("FizzBuzz");
} else {
System.out.println(luku);
}
3 Fizz
4 4
5 Buzz
15 Fizz
Edellisessä lähestymistavassa ongelmana on se, että ehtolauseiden läpikäynti lopetetaan ensimmäiseen ehtoon, jonka arvo on totta. Esimerkiksi luvulla 15 tulostetaan merkkijono "Fizz", sillä luku on kolmella jaollinen (15 % 3 == 0).
Yksi lähestymistapa yllä olevan ajatusketjun kehittämiseen on ensin etsiä vaativin ehto ja toteuttaa se. Tämän jälkeen toteutettaisiin muut ehdot. Yllä olevassa esimerkissä ehto "jos luku on jaollinen kolmella ja viidellä" vaatii kahden tapauksen toteutumista. Nyt ajatusketju olisi muotoa.
- Tee ohjelma, joka lukee luvun käyttäjältä.
- Jos luku on jaollinen kolmella ja viidellä, tulosta luvun sijan merkkijono "FizzBuzz".
- Jos luku on jaollinen kolmella, tulosta luvun sijaan merkkijono "Fizz".
- Jos luku on jaollinen viidellä, tulosta luvun sijaan merkkijono "Buzz".
- Muulloin ohjelma tulostaa käyttäjältä luetun luvun.
Nyt ongelmakin tuntuu ratkeavan.
Scanner lukija = new Scanner(System.in);
int luku = Integer.valueOf(lukija.nextLine());
if (luku % 3 == 0 && luku % 5 == 0) {
System.out.println("FizzBuzz");
} else if (luku % 3 == 0) {
System.out.println("Fizz");
} else if (luku % 5 == 0) {
System.out.println("Buzz");
} else {
System.out.println(luku);
}
2 2
5 Buzz
30 FizzBuzz
Muistathan tarkistaa pistetilanteesi materiaalin oikeassa alareunassa olevasta pallosta!