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.
< 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;
alkuasetus on käsky, joka suoritetaan yhden kerran ennen toistojen aloittamista
ehto tarkistetaan aina ennen jokaista toistokertaa
käskyä toistetaan niin kauan kuin ehto on tosi
etenemiskäsky suoritetaan jokaisen toistokerran jälkeen
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:
Kerroin a = 0 -> yhtälö ei ole toista astetta.
Diskriminantti on suurempi kuin nolla -> yhtälöllä on kaksi reaalijuurta
Diskriminatti on 0 -> yhtälöllä on yksi ratkaisu
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);
}
}
}