Valinta (if)


Tähänastiset ohjelmat ovat toimineet aina samalla tavalla. Usein ohjelman pitäisi muuttaa toimintaansa käyttäjän syötteen, muuttujien arvojen tms mukaan.


Esimerkki: kirjoita ohjelma, joka pyytää käyttäjältä kokonaisluvun ja tulostaa tämän luvun itseisarvon.


Jos annettu luku on negatiivinen, ohjelma tulostaa sen vastaluvun. Muussa tapauksessa voidaan tulostaa annettu luku sellaisenaan.


Valinta voidaan tehdä if-lauseen avulla:


if (ehto)

käsky1;

else

käsky2;


Jos ehto on tosi, suoritetaan käsky1, muussa tapauksessa suoritetaan käsky2

public class Itseisarvo{
  public static void main(String[] args) {
    int luku;

    System.out.println("Anna luku");
    luku = Lue.kluku();
    if (luku < 0)
      System.out.println("Itseisarvo: " + (-luku));
    else
      System.out.println("Itseisarvo: " + luku);
  }
}

else-osa voi myös puuttua. Itseisarvo vähän toisella tavalla:


public class Itseisarvo2{
  public static void main(String[] args) {
    int luku;

    System.out.println("Anna luku");
    luku = Lue.kluku();
    if (luku < 0)
      luku = -luku;
    System.out.println("Itseisarvo: " + luku);
  }
}

Vertailuoperaatioita


Vertailuoperaatioita:


> suurempi kuin

>= suurempi tai yhtäsuuri kuin

< pienempi kuin

<= pienempi tai yhtäsuuri kuin

== yhtäsuuri kuin

!= erisuuri kuin


Vertailujen arvona on totuusarvo true tai false


Huom1: Älä sekoita vertailuoperaatiota == ja sijoitusoperaatiota =


Huom2: Merkkijonojen sisältöjä ei voi vertaila vertailuoperaatioilla


Huom3: Liukulukujen (desimaalilukujen) yhtäsuuruutta ei kannata tutkia


Esimerkkejä vertailuista:

int x = 7;

double d = 5.76


if (x <= 0)

...

if (x != 8)

...

if (d < 0)

...



Loogisia operaatioita


&& "ja"

|| "tai"

^ poissulkeva "tai"

! negaatio, "ei"


Näiden operaatioiden operandit voivat olla vain totuusarvoisia lausekkeita, esim.

  1. < 0) && (y > 10)


Operaatiot && ja || ovat ehdollisia: toisen operandin totuusarvo tutkitaan vain silloin, jos ensimmäisen operandin arvosta ei voida päätellä koko lauseen totuusarvoa.


Esimerkkejä:


int i = 5;

double d = -6.87;

boolean totta;


if ((i < 10) && (d < 0))

...

if ((i < 0) && (d < 0))

...

totta = (i < 10) || (d > 10)

if (totta)

...

if ((! totta) && (d > 0))

...




Lisää valintakäskystä: koottu lause


Kun if-käskyn ehto on tosi, halutaan usein suorittaa useampi kuin yksi käsky.


Tarkastellaan aikaisempaan esimerkkiohjelmaa, joka laski neliön pinta-alan. Vanha ohjelma antoi pinta-alan myös silloin, jos sivun pituus oli negatiivinen.


Muutetaan ohjelmaa niin, että se antaa pinta-alan vain ei-negatiivisille neliöille:


pyydä sivun pituus

jos (sivun pituus >= 0)

laske pinta-ala

tulosta pinta-ala

muuten

ilmoita, että pinta-alaa ei voi laskea


Useampi käsky voidaan sulkujen { } avulla koota yhteen yhdeksi lohkoksi eli kootuksi lauseeksi.




public class Nelio {
  public static void main(String[] args) {
    int sivu, pintaAla;

    System.out.println("Anna neliön sivu:");
    sivu = Lue.kluku();
    if (sivu >= 0) {
      pintaAla = sivu * sivu;
      System.out.println("Pinta-ala on " + pintaAla);
    }
    else 
      System.out.println("Pinta-alaa ei voi laskea");
  }
}

Mitä tapahtuisi, jos sulut puuttuisivat?

If-käskyn sisällä voi olla toinen if-käsky


/* Ohjelma selvittää, onko annettu positiivinen luku parillinen. Ei-positiivisen luvun antaminen on virhe */



public class Parillinenko {
  public static void main(String[] args) {
    double luku;

    System.out.println("Anna positiivinen luku:");
    luku = Lue.dluku();

    if (luku > 0)
      if (luku % 2 == 0) // parillinen
	System.out.println("On parillinen");
      else
	System.out.println("On pariton");
    else
      System.out.println("Luku ei ole posiviivinen!");
  }
}

Koska kaikkiin if-lauseisiin ei sisälly else-osaa, on joskus vaikea tietää, mihin if-lauseeseen joku else-osa liittyy:


if (a < b) if (c < d) e = f; else e = g;


Ongelman ratkaisemiseksi on sovittu sääntö: else-osa liittyy aina lähimpään edeltävään if-lauseeseen, johon ei ole vielä liittynyt else-osaa.


Jos halutaan muuta, voidaan rakennetta muuttaa aaltosuluilla

Usein if-lauseita joudutaan ketjuttamaan


Esimerkki: Opiskelijan arvosana riippuu kokeen pisteistä. Pistemäärällä saa 0-6 hylätyn, 7-12 arvosanan 1, 13-17 arvosanan 2, 18 - 24 arvosanan 3, 25-29 arvosanan 4 ja pisteillä 30-36 arvosanan 5.


if (pisteet >= 0 && pisteet <= 6)

arvos = 0;

else

if (pisteet >= 7 && pisteet <= 12)

arvos = 1;

else

if (pisteet >= 13 && pisteet <= 17)

arvos = 2;

else

if (pisteet >= 18 && pisteet <= 24)

arvos = 3;

else

if (pisteet >= 25 && pisteet <= 29)

arvos = 4;

else

if (pisteet >=30 && pisteet <=36)

arvos = 5;

else

System.out.println("Väärät" +

"pisteet");


Tämä on kyllä oikein, mutta aika epäselvän näköinen. Yleensä tällainen rakenne kirjoitetaan toisin:

if (pisteet >= 0 && pisteet <= 6)

arvos = 0;

else if (pisteet >= 7 && pisteet <= 12)

arvos = 1;

else if (pisteet >= 13 && pisteet <= 17)

arvos = 2;

else if (pisteet >= 18 && pisteet <= 24)

arvos = 3;

else if (pisteet >= 25 && pisteet <= 29)

arvos = 4;

else if (pisteet >= 30 && pisteet <= 36)

arvos = 5;

else

System.out.println("Virheelliset pisteet");

Toisto: while


Esimerkki: tee ohjelma, joka tulostaa kuuden kertotaulun.


Tähän asti opituilla välineillä jokaisen kertotaulun luvun laskemista ja tulostamista varten pitää kirjoittaa oma käsky.


...

System.out.println(1 * 6);

System.out.println(2 * 6);

...

System.out.println(10 * 6);


Toistokäskyn avulla sama asia voidaan tehdä selvästi helpommalla


Yleinen rakenne on

while (ehto)

käsky;


käskyä suoritetaan niin kauan kuin ehto on tosi.


Kuuden kertotaulu voidaan nyt tulostaa seuraavalla silmukalla:


i = 1;

while (i <= 10) {

System.out.println(i * 6);

i++;

}


Koska toistettavana on kaksi käskyä, pitää ne koota yhteen aaltosuluilla.

Toinen esimerkki: ohjelma pyytää käyttäjältä kokonaisluvun ja laskee tämän luvun kertoman.


(Tässä ohjelmassa käytetään kertoman tallentamiseen int-tyyppiä. Sen vuoksi ohjelma voi laskea korkeintaan 12:n kertomia.)



public class Kertoma {
  public static void main(String[] args) {
    int i, luku, kertoma;

    System.out.println("Anna luku: ");
    luku = Lue.kluku();
    if (luku < 0 || luku > 12)
      System.out.println("Ohjelma ei voi laskea kertomaa");
    else {
      kertoma = 1;
      i = 1;
      while (i <= luku) {
	kertoma = kertoma * i;
	i++;
      }
      System.out.println("Kertoma on " + kertoma);
    }
  }
}


Toisto: for-lause


Toinen toistorakenne on for-lause. Lauseen yleinen muoto on


for (alkuasetus; ehto; etenemiskäsky)

käsky;



Esimerkiksi kuuden kertotaulu for-käskyn avulla:


int i;

for (i = 1; i <= 10; i++)

System.out.println(i * 6);



kertoma for-käskyn avulla:



public class Kertoma2 {
  public static void main(String[] args) {
    int i, luku, kertoma;

    System.out.println("Anna luku: ");
    luku = Lue.kluku();
    if (luku < 0 || luku > 12)
      System.out.println("Ohjelma ei voi laskea kertomaa");
    else {
      kertoma = 1;
      for (i = 1; i <= luku; i++)
	kertoma = kertoma * i;
      System.out.println("Kertoma on " + kertoma);
    }
  }
}

Silmukkamuuttujaa ei tarvitse välttämättä kasvattaa, vaan sitä voidaan muuttaa myös muilla tavoin. Esimerkiksi kertoma vähän toisella tavalla:



public class Kertoma3 {
  public static void main(String[] args) {
    int i, luku, kertoma;

    System.out.println("Anna luku: ");
    luku = Lue.kluku();
    if (luku < 0 || luku > 12)
      System.out.println("Ohjelma ei voi laskea kertomaa");
    else {
      kertoma = 1;
      for (i = luku; i >= 1; i--)
	kertoma = kertoma * i;
      System.out.println("Kertoma on " + kertoma);
    }
  }
}

Milloin tulee käyttää for-käskyä, milloin while-käskyä?


For-käsky sopii lähinnä sellaisiin tilanteisiin, jossa silmukkamuuttujan muutokset ovat suhteellisen yksinkertaisia. Tällöin voi käyttää myös while-käskyä, kysymys on ohjelmoijan mieltymyksestä.


Jos silmukkamuuttujan muutokset ovat monimutkaisempia, while-käsky on yleensä selkeämpi.



Toisto: do-while


do-while-rakenteessa toistettava käsky suoritetaan aina vähintään kerran. Rakenne sopii esimerkiksi tilanteisiin, joissa syöte luetaan aina vähintään kerran.


Yleinen rakenne:

do

käsky

while (ehto)


Ensin suoritetaan käsky. Sitten tutkitaan ehdon totuusarvo. Niin kauan kuin ehto on tosi, käsky suoritetaan uudelleen.


Kuuden kertotaulu do-while-rakenteella:


int i;

i = 1;

do {

System.out.println(i * 6);

i++;

} while (i <= 10);






Esimerkki: ei-negatiivisten lukujen keskiarvo. Käyttäjä voi syöttää mielivaltaisen määrän ei-negatiivisia lukuja. Ohjelma laskee niiden keskiarvon. Lukujen syöttäminen lopetetaan antamalla negatiivinen luku



public class Keskiarvo {
  public static void main(String[] args) {
    
    int lkm = 0;
    double luku, summa = 0, keskiarvo;

    System.out.println("Anna lukuja, lopeta negatiivisella");
    do {
      luku = Lue.dluku();
      if (luku >= 0) {
	lkm++;
	summa = summa + luku;
      }
    } while (luku >= 0);

    if (lkm == 0)
      System.out.println("Ei yhtaan lukua");
    else {
      keskiarvo = summa / lkm;
      System.out.println("Keskiarvo on " + keskiarvo);
    }
  }
}

Esimerkki: toisen asteen yhtälön ratkaisu


Kirjoitetaan ohjelma, joka ratkaisee toisen asteen yhtälön

ax**2 + bx +c = 0


Tunnetusti yhtälöllä on ratkaisukaava


x1 = (-b + sqrt(b**2 + 4ac)) / 2a
ja
x2 = (-b - sqrt(b**2 + 4ac)) / 2a




Neliöjuuren alla olevaa lauseketta kutsutaan diskriminantiksi.


Ohjelma pyytää aluksi käyttäjältä yhtälön kertoimet a, b, ja c. Sen jälkeen käydään läpi eri tapaukset:


  1. Kerroin a = 0 -> yhtälö ei ole toista astetta.

  2. Diskriminantti on suurempi kuin nolla -> yhtälöllä on kaksi reaalijuurta

  3. Diskriminatti on 0 -> yhtälöllä on yksi ratkaisu

  4. Diskriminatti on pienempi kuin 0 -> yhtälöllä on kaksi imaginaarista ratkaisua


Neliöjuuren laskemiseen voidaan käyttää javan Math-luokassa olevaa sqrt-metodia. Esimerkiksi muuttujan luku neliöjuuri voidaan laskea käskyllä Math.sqrt(luku)


public class ToinenAste{
  public static void main(String args[]) {

    int a, b, c, disk;
    double x1, x2, reaali, imag;

    System.out.println("Anna kertoimet a, b ja c:");
    a = Lue.kluku();
    b = Lue.kluku();
    c = Lue.kluku();
    disk = b * b - 4 * a * c;
    if (a==0) 
      System.out.println("Ei ole toisen asteen yhtälö");
    else if (disk > 0) {
      x1 = (-b + Math.sqrt(disk)) / (2.0 * a);
      x2 = (-b - Math.sqrt(disk)) / (2.0 * a);
      System.out.println("Juuret ovat "+x1+" ja "+x2);
    }
    else if (disk == 0) {
      x1 = -b / (2.0 * a);
      System.out.println("Ratkaisu on "+x1);
    }
    else { // imaginaariratkaisu
      reaali = -b / (2.0 * a);
      imag = Math.sqrt(-disk) / (2.0 * a);
      System.out.println("Juuret ovat "+reaali +"+"+imag+"i ja "+reaali +
			 "-"+imag+"i");
    }
  }
}

Fibonaccin luvut


Fibonaccin lukujono on 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ... siten, että seuraava luku on aina kahden edellisen luvun summa.


Kirjoitetaan ohjelma, joka laskee Fibonaccin n:nnen luvun (käyttäjä antaa n:n).



public class Fibonacci {
  public static void main(String[] args) {
    double uusiFib, vanhaFib, summa;
    int luku, i;

    System.out.println("Anna luku:");
    luku = Lue.kluku();
    if (luku <= 0) 
      System.out.println("Ei Fibonaccin lukua");
    else if (luku == 1 || luku == 2)
      System.out.println("Fibonaccin luku: " + luku);
    else {
      uusiFib = 1;
      vanhaFib = 1;
      for (i = 3; i <= luku; i++) {
	summa = uusiFib + vanhaFib;
	vanhaFib = uusiFib;
	uusiFib = summa;
      }
      System.out.println("Fibonaccin luku: " + uusiFib);
    }
  }
}

Esimerkki: potenssiin korotus


Tehdään ohjelma, joka korottaa liukuluvun a kokonaislukupotenssiin b:



public class Potenssi1 {
  public static void main(String[] args) {
    int exp, i;
    double kanta, tulos;

    System.out.println("Anna kantaluku ja eksponentti");
    kanta = Lue.dluku();
    exp = Lue.kluku();
    if (exp <= 0) 
      System.out.println("Liian pieni eksponentti");
    else {
      tulos = 1.0;
      for (i = 1; i <= exp; i++)
	tulos = tulos * kanta;
      System.out.println(kanta + " potenssiin " + exp + " on " + tulos);
    }
  }
}

Tehokkaampi potenssiin korotus




public class Potenssi2 {
  public static void main(String[] args) {
    int exp, i;
    double kanta, tulos;

    System.out.println("Anna kantaluku ja eksponentti");
    kanta = Lue.dluku();
    exp = Lue.kluku();
    if (exp <= 0) 
      System.out.println("Liian pieni eksponentti");
    else {
      tulos = 1.0;
      while (exp > 0) 
	if (exp % 2 == 0) {
	  kanta = kanta * kanta;
	  exp = exp / 2;
	}
	else {
	  tulos = tulos * kanta;
	  exp = exp -1;
	}
      System.out.println("Tulos on "+ tulos);
    }
  }
}