Ohjelmoinnin peruskäsitteet:

Luku D
Ohjausrakenteet

  1. Suoritusjärjestyksen ohjaaminen
  2. Vertailulausekkeet ja totuusarvot
  3. Ehtolause
  4. Toistolauseet

1. Suoritusjärjestyksen ohjaaminen

Yksinkertaisiksi lauseiksi kutsutaan sellaisia ohjelmointikielen lauseita, jotka tekevät "yhden asian yhden kerran". Esimerkiksi muuttujan määrittely, arvon sijoittaminen muuttujaan ja tulostus ovat tällaisia yksinkertaisia lauseita.

int x;
x = 10;
System.out.println(x);

Lauseet suoritetaan järjestyksessä yksi kerrallaan. Tätä kutsutaan peräkkäisrakenteeksi ja sen voidaan ajatella olevan ensimmäinen ohjausrakenne.

Yksinkertaisilla lauseilla on mahdollista tehdä toimivia ohjelmia. Ohjelmat jäävät kuitenkin välttämättä varsin yksinkertaisiksi, koska melkein kaikissa algoritmeissa tarvitaan ehdollisuutta ja toistoa.

Esimerkiksi kymmenen arvon järjestämisessä tarvitaan (käytettäessä kuplalajittelua) 9 + 8 + ... + 1 vertailua, jotka pitäisi peräkkäisrakenteena toteutettuna todella kirjoittaa näkyville. Kymmenelle arvolle tehty ohjelma ei myöskään toimisi esimerkiksi 11 arvolle. Toistorakenteen avulla lajittelu voidaan toteuttaa kahdella sisäkkäisellä toistolla ja se toimii kaikille arvojen lukumäärille.

Java-kielessä ehdollisuus toteutetaan if-lauseella eli ehtolauseella. Toisto toteutetaan while-lauseella tai for-lauseella eli toistolauseilla.

2. Vertailulausekkeet ja totuusarvot

Ehdollisuus voidaan usein kuvata sanoilla:

jos ehto pitää paikkansa, niin tehdään toiminto

Toisto voidaan kuvata sanoilla:

niin kauan kuin ehto pitää paikkansa, tehdään toiminto uudestaan

Kummassakin edellisessä esiintyy ehto, joka lopulta ratkaisee toiminnon suorittamisen.

Ohjelmoinnissa ehto on lauseke, jonka arvona on totuusarvo (tosi tai epätosi). Javassa tosuuarvo esitetään boolean-tietotyypin avullaja on joko true tai false.

Tavallisimpia totuusarvoisia lausekkeita ovat vertailulausekkeet. Niissä verrataan kahta arvoa toisiinsa vertailuoperaattorin avulla. Vertailuoperaattoreita ovat

LausekeMerkitys
a == b a on yhtäsuuri kuin b
a != ba on erisuuri kuin b
a < ba on pienempi kuin b
a > ba on suurempi kuin b
a <= b a on pienempi tai yhtäsuuri kuin b
a >= b a on suurempi tai yhtäsuuri kuin b

Yhtäsuuruusvertailun == sekoittaa helposti sijoitukseen =. Ne ovat aivan eri asia!

Vertailuoperaattoreita voi käyttää mihin tahansa perustietotyyppeihin. Merkkijonojen vertailussa pitää käyttää erilaista tapaa

a.equals(b) a on sama merkkijono kuin b
!a.equals(b) a on eri merkkijono kuin b
a.compareTo(b) < 0 a on aakkosjärjestyksessä edellä merkkijonoa b
a.compareTo(b) > 0 a on aakkosjärjestyksessä jäljessä merkkijonoa b

Vertailulausekkeita (ja muita totuusarvoisia lausekkeita) voi edelleen käyttää loogisten operaattorien kanssa.

Operaattori ! (looginen ei) vaihtaa lausekkeen totuusarvon. Esimerkiksi a <= b voidaan kirjoittaa myös !(a > b). Katso myös merkkijonojen erisuuruuden tutkiminen yllä.

Operaattori && (looginen ja) yhdistää kaksi totuusarvoa siten, että koko lauseke on tosi vain jos kumpikin yhdistetty lauseke on tosi. Esimerkiksi matemaattinen ilmaus 0 < x < 10 kirjoitetaan Java-kielellä 0 < x && x < 10 (edellyttäen, että muuttuja x on määritelty kokonaisluvuksi).

Operaattori || (looginen tai) yhdistää kaksi tosuuarvoa siten, että koko lauseke on tosi jos jompikumpi yhdistetyistä on tosi tai kumpikin yhdistetyistä on tosi. Esimerkiksi, jos halutaan osoittaa, että muuttujan x arvo ei ole välillä 0 - 10, kirjoitetaan x < 0 || x > 10.

3. Ehtolause

Ehtolause on muotoa

if (ehto) {
  lauseet
}

ja merkitys on "jos ehto on tosi, niin lauseet suoritetaan".

Sulut {} voi jättää pois, jos lauseita on vain yksi.

Esimerkkiohjelma laskee annetut kaksi lukua yhteen, jos ne todella on annettu.

 1:class Summa2
 2:{
 3:   public static void main(String[] args)
 4:   {
 5:      if (args.length == 2) {
 6:         int a = Integer.parseInt(args[0]);
 7:         int b = Integer.parseInt(args[1]);
 8:         int summa = a + b;
 9:         System.out.println("  " + a + " + " + b + " = " + summa);
10:      }
11:   }
12:}

Annettujen komentoriviparametrien määrä saadaan selville lausekkeella args.length (lisätietoja).

Vertaa ohjelmaa aikaisemmin esitettyyn esimerkkiin, jossa lukujen puuttuminen aiheutti ruman virheilmoituksen.

Tehtävä D1: Tee ohjelma "Jarjesta3", jolle annetaan kolme lukua, jotka se tulostaa nousevassa suuruusjärjestyksessä. Algoritmi löytyy korttiprosessorista. Ohjelma käytössä:
C:\java> java Jarjesta3 51 23 40
23 40 51

C:\java>

else-osa

Edellinen esimerkki on hieman tylysti toteutettu, koska se ei kerro miksi mitään ei tapahdu ilman annettuja lukuja. Tämä voidaan korjata käyttämällä if-lauseen laajennettua muotoa

if (ehto) {
   lauseet
} else {
   vaihtoehtoiset lauseet
}

jonka merkitys on "jos ehto on tosi, niin suoritetaan lauseet, muutoin suoritetaan vaihtoehtoiset lauseet".

Nyt esimerkkiin voidaan lisätä virheilmoitus:

 1:class Summa2
 2:{
 3:   public static void main(String[] args)
 4:   {
 5:      if (args.length == 2) {
 6:         int a = Integer.parseInt(args[0]);
 7:         int b = Integer.parseInt(args[1]);
 8:         int summa = a + b;
 9:         System.out.println("  " + a + " + " + b + " = " + summa);
10:      } else {
11:         System.out.println("  Tarvitaan kaksi lukua!");
12:      }
13:   }
14:}

else if -osat

Ehtolauseita voi ketjuttaa peräkkäin, jolloin lauseen muoto on

if (ehto1) {
  lauseet1
} else if (ehto2) {
  lauseet2
} else if (ehto3) {
  lauseet3
}
...
} else {
  vaihtoehtoiset lauseet
}

ja merkitys on "jos ehto1 on tosi, niin suoritetaan lauseet1, muutoin jos ehto2 on tosi, niin suoritetaan lauseet2, muutoin jos ehto3 on tosi, niin suoritetaan lauseet3, muutoin suoritetaan vaihtoehtoiset lauseet".

Tehtävä D2: Tee ohjelma, jolle annetaan kaksi, kolme tai neljä kokonaislukua. Ohjelma tulostaa lukujen summan tai virheilmoituksen, jos lukuja on jokin muu määrä.

4. Toistolauseet

Toistolauseita esitellään tässä kaksi kappaletta: while-lause ja for-lause. Kaikki alhoritmeissa esiintyvät toistot voidaan toteuttaa toisella käyttämällä vain toista näistä lausetyypeistä. Lauseen valinnassa onkin kysymys lähinnä sopivuudesta ja myös makuasioista.

while-lause

Yksinkertaisemman toistolauseen muoto on

while (ehto) {
  lauseet;
}

ja merkitys on "niin kauan kuin ehto pitää paikkansa, tehdään lauseet uudestaan. Ehto tutkitaan aina ennen lauseita eli järjestys on

Toistolauseen sisällä olevien lauseiden pitäisi jollakin tavalla muuttaa ehdossa käytettävien muutujien arvoja. Muussa tapauksessa voi syntyä päättymätön silmukka (ehto on aina tosi).

Ensimmäinen esimerkki tulostaa yksinkertaisesti parametrina annetun määrän rivejä. Koodi on tiedostossa "Rivit.java".

 1:class Rivit {
 2:   
 3:   public static void main (String[] args) {
 4:      // luetaan rivien lukumäärä komentoriviltä
 5:      int lkm = Integer.parseInt(args[0]);
 6:      // tulostetaan rivit
 7:      int i = 0;
 8:      while (i < lkm) {
 9:         System.out.println("Rivi " + i);
10:         i++;
11:      }
12:   }
13:}

Huomaa apumuuttujan i käyttö toisto toteutuksessa. Sen arvoa muutetaan toistolauseen sisällä (i++). Toistolauseen ehto tulee näin väkisin epätodeksi, kun haluttu määrä kierroksia on saatu täyteen. Esimerkki ohjelman käytöstä (käännös on tässä tehty aiemmin) on alla.

C:\java> java Rivit 5
Rivi 0
Rivi 1
Rivi 2
Rivi 3
Rivi 4

C:\java>

Toinen esimerkki on sovelma, joka piirtää toistuvan kuvion. Aluksi koodi tiedostossa "PalloSovelma.java".

 1:import java.awt.*;
 2:import java.applet.*;
 3:
 4:public class PalloSovelma extends Applet {
 5:   
 6:   public void paint(Graphics g) {
 7:      // piirretään kymmenen palloa kasvavalla koolla
 8:      int i = 0;
 9:      int paikka = 10;
10:      int koko = 10;
11:      while (i < 10) {
12:         g.fillOval(paikka, paikka, koko, koko);
13:         paikka += 20;
14:         koko += 10;
15:         i++;
16:      }
17:   }
18:}

Kerrataan tarvittava koodi sovelman lisäämiseksi sivulle. Sovelma on alihakemistossa "java". Katso ohjeet sovelman kääntämisestä aiemmasta materiaalista.

<applet codebase="java" code="PalloSovelma.class" width="300" height="300">
   Sovelmat eivät toimi tässä selaimessa.
</applet>

Aiemmin havaittujen WebCT/Applet -ongelmien vuoksi näytetään tässä vain kuva sovelmasta. Kuva on otettu IE 6.0 -selaimesta.

Kuva sovelmasta toiminnassa Kuva sovelmasta, ei itse sovelma.

for-lause

Huomattava osa toistolauseista on muotoa

alustus;
while (ehto) {
   lauseet;
   muutos;
}

jossa alustus antaa toistossa käytettäville (apu)muuttujille alkuarvot ja muutos muuttaa em. muuttujien arvoja.

Tällaiselle toistolauseelle on olemassa tiiviimpi muoto:

for (alustus; ehto; muutos) {
   lauseet;
}

Esimerkiksi yllä annetussa rivejä tulostavassa sovelluksessa käytetty while-lause voidaan kirjoittaa muotoon:

for (int i = 0; i < lkm; i++) {
   System.out.println("Rivi" + i);
}

Toistolauseilla ei ole mitään merkittävää toiminnallista eroa. Monien mielestä for-lause soveltuu parhaiten taulukoiden läpikäyntiin.

Tehtävä D3: Tee sovellus "Summaa", joka laskee kaikki komentoriviltä annetut luvut yhteen ja tulostaa summan. Vihje: komentoriviparametrit voit käydä läpi lauseella:
for (int i = 0; i < args.length; i++) {
   // nyt parametrin saa kokonaislukuna lausekkeella
   int luku = Integer.parseInt(args[i]);
}

Tehtävä D4: Tee "ShakkiSovelma", jossa piirretään 8X8 -kokoinen shakkiruudukko ilman kirjaimia ja numeroita. Käytä piirtämisessä sisäkkäisiä toistolauseita:
for (int rivi = 0; rivi < 8; rivi++) {
   for (int sarake = 0; sarake < 8; sarake++) {
      // piirrä yksi ruutu tässä
      // kuinka selvität mustat ja valkoiset?
   }
}

Toistolauseet ja taulukot

Toistolauseen käyttö on välttämätöntä useimmissa taulukoita käyttävissä sovelluksissa. Tässä esitettävissä esimerkeissä käsitellään kokonaislukuja sisältäviä taulukoita, joihin sisältö saadaan komentoriviltä. Aluksi yksinkertainen ohjelma, joka kopioi komentorivin kokonaislukutaulukkoon ja tulostaa sen näkyville. Tiedostossa "Luvut.java":

 1:class Luvut {
 2:   
 3:   public static void main (String[] args) {
 4:      // luodaan ensin sopivan kokoinen taulukko
 5:      // args.length on komentorivin parametrien lukumäärä
 6:      int[] luvut = new int[args.length];
 7:      // kopioidaan taulukosta toiseen ja muutetaan kokonaisluvuiksi
 8:      for (int i = 0; i < args.length; i++) {
 9:         // kopioidaan kohdasta i kohtaan i
10:         luvut[i] = Integer.parseInt(args[i]);
11:      }
12:      // tulostetaan jokainen luku omalle rivilleen indeksin kanssa
13:      for (int i = 0; i < luvut.length; i++) {
14:         System.out.println(i + ": " + luvut[i]);
15:      }
16:   }
17:}

Esimerkki ohjelman käytöstä:

C:\java> java Luvut 45 12 32 7 123
0: 45
1: 12
2: 32
3: 7
4: 123

C:\java>

Etsintä taulukosta voidaan toteuttaa samankaltaiselle algoritmilla. Seuraava esimerkki tulostaa komentoriviparametreista pienimmän. Tiedostossa "Pienin.java":

 1:class Pienin {
 2:   
 3:   public static void main (String[] args) {
 4:      int[] luvut = new int[args.length];
 5:      for (int i = 0; i < args.length; i++) {
 6:         luvut[i] = Integer.parseInt(args[i]);
 7:      }
 8:      // etsitään pienin
 9:      // oletetaan, että se on ensimmäinen
10:      int pienin = luvut[0];
11:      // tutkitaan lopputaulukko toisesta alkiosta eteenpäin
12:      for (int i = 1; i < luvut.length; i++) {
13:         // jos löydetty pienempi kuin aiemmat
14:         if (luvut[i] < pienin) {
15:            pienin = luvut[i];
16:         }
17:      }
18:      // tulostetaan pienin
19:      System.out.println("Pienin on " + pienin);
20:   }
21:}

Esimerkki ohjelman käytöstä seuraa:

C:\java> java Pienin 56 12 34 9 12 78
Pienin on 9

C:\java>

Lopuksi vielä lukujen järjestäminen valintalajittelua käyttämällä. Siinä etsitään aina lopputaulukosta pienin ja laitetaan se oikealle paikalleen. Tiedostossa "Jarjesta.java":

 1:class Jarjesta {
 2:   
 3:   public static void main (String[] args) {
 4:      int[] luvut = new int[args.length];
 5:      for (int i = 0; i < args.length; i++) {
 6:         luvut[i] = Integer.parseInt(args[i]);
 7:      }
 8:      
 9:      // järjestetään luvut
10:      // jokaisella kierroksella laitetaan yksi alkio paikalle p
11:      for (int p = 0; p < luvut.length-1; p++) {
12:         // oletetaan, että pienin on paikassa p
13:         int pienin = p;
14:         // tutkitaan loput taulukon alkiot
15:         for (int i = p+1; i < luvut.length; i++) {
16:            // jos pienempi löytyy, laitetaan paikka talteen
17:            if (luvut[i] < luvut[pienin]) {
18:               pienin = i;
19:            }
20:         }
21:         // vaihdetaan pienin alkuun ellei jo ole
22:         if (pienin != p) {
23:            // vaihto apumuuttujan avulla
24:            int apu = luvut[p];
25:            luvut[p] = luvut[pienin];
26:            luvut[pienin] = apu;
27:         }
28:      }
29:      
30:      // tulostetaan järjestettynä
31:      for (int i = 0; i < luvut.length; i++) {
32:         System.out.println(i + ": " + luvut[i]);
33:      }
34:   }
35:}

Esimerkki ohjelman käytöstä:

C:\java> java Jarjesta 23 1 67 34 2 89 24 3 2 50
0: 1
1: 2
2: 2
3: 3
4: 23
5: 24
6: 34
7: 50
8: 67
9: 89

C:\java>

Edellinen lajittelualgoritmi on kuvattu korttiprosessorilla.

Tehtävä D5: Tee sovellus "Tilastot", joka laskee komentoriviltä saamistaan luvuista keskiarvon (summa/lkm), suurimman luvun, pienimmän luvun ja mediaanin (järjestyksessä keskimmäinen tai kahden keskimmäisen keskiarvo). Sovellus tulostaa tiedot siistissä muodossa.