Tämä ei ole uusin versio kurssista. Katso myös Ohjelmoinnin MOOC 2019: https://ohjelmointi-19.mooc.fi.
Tehtävät

Lyhyt aloituskysely

Toisen osan tavoitteet

Ymmärtää vuokaavioiden idean ja osaa piirtää vuokaavioita annetusta ohjelmasta. Hahmottaa ja tunnistaa ohjelman käyttämien muuttujien tilan ohjelman eri suoritusvaiheessa. Osaa soveltaa toistolausetta ohjelmointiongelmissa. Tuntee käsitteet metodi, parametri ja kutsupino. Osaa luoda ja käyttää parametrillisia metodeja.

Muutama hyödyllinen vinkki ohjelmointiin NetBeansissa

Tässä muutamia hyödyllisiä vinkkejä ohjelmointiin. Vinkit ovat sekä yleisiä että NetBeansin käyttöön liittyviä.

  1. Vältä punaista!

    Kun NetBeans alleviivaa yhden tai useamman rivin punaisella värillä ja lähdekoodi-ikkunan vasemmassa ja oikeassa laidassa on punaista, NetBeans kertoo että ohjelmasi ei ole toimivassa tilassa. Ohjelmaasi ei voi myöskään tällöin suorittaa.

    Kuva NetBeans-editorista, missä koodi on kääntymättömässä tilassa.

     

    Ylläolevassa kuvassa NetBeans korostaa kahta virheellistä kohtaa. Ensimmäisessä kohdassa ehtolauseen ehdosta puuttuu mm. sulut. Toisessa kohdassa yritetään käyttää muuttujaa nimeltä luk, vaikkei sellaista ole.

    Kun punaisten kohtien määrä kasvaa, etkä saa ohjelmaasi toimimaan kuten haluat, pysähdy. Poista tekemiäsi muutoksia (ctrl + z) kunnes ohjelmointiympäristö ei valita lähdekoodissa olevista virheistä.

    Kun virheitä ei näy, hengitä ja kerro ääneen mitä haluat tehdä. Mikä on tavoitteesi ja minkälaisia pienempiä tavoitteita tavoitteesi saavuttamiseksi tulee tehdä? Etene pienin askelin ja toteuta ohjelmaasi osa kerrallaan. Ennen pitkää pienet osat tulevat sinulle tutuksi, ja opit yhä paremmin yhdistelemään niitä kokonaisiksi ohjelmiksi.

    Kuva NetBeans-editorista, missä koodi kääntyy.

     

    Tehtävien uudelleen alusta aloittamisessa ei ole mitään pahaa. Päinvastoin, kertaat tällöin ohjelman rakenteen toteuttamista, ja annat itsellesi aikaa oppia. Ohjelmakoodin lisääminen toimimattomaan ohjelmakoodiin johtaa harvoin toimivaan ratkaisuun.

     

  2. Opettele hyödyntämään näppärää aaltosulkujen täydennystä

    Eräs aloittelevan ohjelmoijan tyypillinen syy "punaisen koodin" lisääntymiselle on aaltosulkujen liikakäyttö tai niiden liian vähäinen käyttö. Jokaiselle aaltosululle tulee löytyä ohjelmasta pari, sillä ne määrittelevät lohkoja, joissa ohjelmakoodia suoritetaan.

    NetBeans auttaa tässä huomattavasti. Kun kirjoitat ehtolauseen, siihen liittyvän ehdon, sekä avaavan aaltosulkeen, koodi näyttää esimerkiksi seuraavalta:

    Kuva NetBeans-editorista, missä yksi ylimääräinen avonainen aaltosulku.

     

    Koodi ei käänny -- NetBeans vinkkaa, että virhe on viimeisellä rivillä. Syy on kuitenkin puuttuva aaltosulku.

    Jos painat heti avaavan aaltosulkeen kirjoittamisen jälkeen enteriä, NetBeans luo kirjoittamallesi aaltosululle parin.

    Kuva NetBeans-editorista, missä yksi ylimääräinen avonainen aaltosulku.

    Punaista ei näy ja voit edetä rauhassa.

     

  3. Opettele käyttämään koodin automaattista täydennystä

    Jos olet esitellyt ohjelmassasi muuttujan, ei sen nimeä tarvitse kirjoittaa joka kerta kokonaan.

    Kokeile mitä tapahtuu kun kirjoitat muuttujan ensimmäisen kirjaimen ja painat sen jälkeen yhtäaikaa ctrl ja välilyönti. HUOM: joissakin käyttöjärjestelmissä automaattinen täydennys saadaan aikaan painamalla yhtä aikaa ctrl, alt ja välilyönti.

    Allaolevassa esimerkissä ohjelmoija on luonut muuttujan sukunimi, jonka jälkeen hän kirjoittaa merkin s ja valitsee näppäinyhdistelmällä automaattisen täydennyksen.

    Kuva NetBeans-editorista, demonstroidaan automaattista koodin täydennystä.

     

    Muuttujan sukunimi voi valita NetBeansin tarjoamasta listasta. NetBeans osaa täydentää muitakin nimiä sekä avainsanoja, esimerkiksi muuttujan tyypin double aikaansaamiseksi riittää kirjoittaa w sekä valita automaattinen täydennys.

     

  4. Käytä lyhennettä sout

    Muista että saat tehtyä tulostuskomennon System.out.println("") kirjoittamalla sout ja painamalla tabulaattoria eli q:n vasemmalla puolella olevaa näppäintä.

Vuokaaviot ja ohjelman suorituksen visualisointi

Ensimmäisessä osassa tutustuttiin neljään ohjelmoinnin pääteemaan. Tiedon tallentamiseen ja muistamiseen (muuttujat), interaktiivisten ohjelmien luomiseen (syötteen lukeminen käyttäjältä), valintojen tekemiseen ohjelmassa (ehtolauseet) sekä asioiden toistamiseen (toistolauseet).

Tutustutaan seuraavaksi vuokaavioihin. Vuokaavio on kaaviotyyppi, jonka avulla voidaan kuvata (mm.) tietokoneohjelman suoritusta visuaalisessa muodossa. Vuokaavio koostuu suorituksen alku- ja loppupisteistä (ellipsit), suoritettavista lauseista (suorakulmiot), tulostus- ja lukulauseista (suunnikkaat) sekä päätöksenteosta (salmiakki). Nuolilla kuvataan ohjelman suorituksen kulkua.

Alla on esimerkkinä muutamia ohjelmia sekä niihin liittyviä vuokaavioita.

Vuokaavio ja ehtolauseet

Alla on kuvattuna kaksi esimerkkiä vuokaavion käytöstä ehtolauseiden kanssa.

Scanner lukija = new Scanner(System.in);

int luku = Integer.parseInt(lukija.nextLine());

if (luku == 0) {
    System.out.println("Luettu luku oli 0");
} else {
    System.out.println("Luettu luku ei ollut 0");
}
Tehty osoitteessa flowchart.js.org. Koodi: st=>start: Alku

				    op1=>inputoutput: lue luku
				    cond=>condition: luku == 0
				    cond2=>condition: luku > 5

				    io=>inputoutput: "Luettu luku oli 0"

				    io2=>inputoutput: "Luettu luku ei ollut 0"

				    e=>end: Loppu

				    st->op1->cond
				    cond(yes)->io->e
				    cond(no)->io2->e
Scanner lukija = new Scanner(System.in);

int luku = Integer.parseInt(lukija.nextLine());

if (luku == 0) {
    System.out.println("Luettu luku oli 0");
} else if (luku > 5) {
    System.out.println("Luettu luku oli suurempi kuin 5");
} else {
    System.out.println("Luettu luku ei ollut 0 eikä suurempi kuin 5");
}
Tehty osoitteessa flowchart.js.org. Koodi: st=>start: Alku

				    op1=>inputoutput: lue luku
				    cond=>condition: luku == 0
				    cond2=>condition: luku > 5

				    io=>inputoutput: "Luettu luku oli 0"

				    io2=>inputoutput: "Luettu luku ei ollut 0 eikä suurempi kuin 5"

				    io3=>inputoutput: "Luettu luku oli suurempi kuin 5"

				    e=>end: Loppu

				    st->op1->cond
				    cond(yes)->io->e
				    cond(no)->cond2

				    cond2(yes)->io3->e
				    cond2(no)->io2->e

Vuokaavio toistolauseiden kanssa

Alla on kuvattuna kaksi esimerkkiä vuokaavion käytöstä toistolauseiden kanssa.

Scanner lukija = new Scanner(System.in);

while (true) {
    System.out.print("Jatketaanko (luku 1 jatkaa)? ");
    int komento = Integer.parseInt(lukija.nextLine());

    if (komento != 1) {
        break;
    }

    System.out.println("Jatketaan!");
}

// toistolauseen lohkoa seuraava komento
System.out.println("Kiitos!");
Tehty osoitteessa Flowchart.js.org. Koodi: st=>start: Alku
				    e=>end: Loppu

				    io2=>inputoutput: "Jatketaanko (luku 1 jatkaa)?"
     io3=>inputoutput: "Jatketaan!"
io4=>inputoutput: "Kiitos!"

io5=>inputoutput: lue komento

cond=>condition: komento != 1

st->io2->io5->cond
cond(yes)->io4->e

cond(no)->io3->io2
int luku = 0;

while (true) {
    luku++;
    if (luku > 10) {
        break;
    }

    System.out.println("Luku nyt: " + luku);
}

// toistolauseen lohkoa seuraava komento
System.out.println("Kiitos!");
Tehty osoitteessa Flowchart.js.org. Koodi: st=>start: Alku
				    e=>end: Loppu

				    op=>operation: luku=0
				    op2=>operation: luku++

				    cond=>condition: luku > 10

				    io2=>inputoutput: "Luku nyt: " + luku
				    io4=>inputoutput: "Kiitos!"

				    st->op->op2->cond
				    cond(yes)->io4->e
				    cond(no)->io2->op2

Kuten huomaat, vuokaavioon ei merkitä jokaista ohjelman lausetta tai lauseketta. Kaavion yksityiskohtaisuus on myös tekijän vastuulla -- esimerkiksi yllä olevan ohjelman voisi toteuttaa useammalla eri tavalla.

Muuttuvat muuttujat

Syvennytään tässä vielä tarkemmin muuttujien sielunelämään, niiden olemassaoloon sekä niissä olevien arvojen muuttamiseen.

Muuttujan arvon muuttaminen

Muuttujia käytetään tiedon säilyttämiseen sekä tiedon myöhempään noutamiseen. Muuttujan esittely tapahtui kertomalla ensin muuttujan tyyppi, esimerkiksi kokonaisluku (int), jota seuraa muuttujan nimi, esimerkiksi luku -- yhdessä int luku. Muuttujan esittelyä seuraa tyypillisesti sijoitusoperaatio, minkä avulla muuttujalle asetetaan arvo.

Esimerkiksi lause int luku = 5; luo kokonaislukumuuttujan nimeltä luku ja asettaa siihen arvon 5.

Olemassaolevan muuttujan arvoa voi käyttää uuden muuttujan esittelyssä ja arvon asetuksessa. Alla olevassa esimerkissä luodaan ensin muuttuja nimeltä pituus, jonka jälkeen luodaan muuttuja paino. Muuttujaan paino asetetaan lausekkeen pituus / 2 evaluaation tulos, eli puolet muuttujan pituus arvosta.

int pituus = 166;
int paino = pituus / 2;

System.out.println(paino);
83

Muuttujan arvoa voidaan muuttaa ohjelman aikana eli muuttujaan voi asettaa myös uuden arvon. Tämä onnistuu sijoituslauseen avulla. Seuraavassa esitellään ensin muuttuja ika, jolle asetetaan alkuarvoksi 1. Tämän jälkeen muuttujan ika arvoa kasvatetaan yhdellä:

int ika = 1;

System.out.println(ika);
ika = ika + 1;    // muuttujan ika uusi arvo on ika-muuttujan vanha arvo plus yksi
System.out.println(ika);
1
2

Tarkastellaan lausetta ika = ika + 1; tarkemmin. Lause ika = ika + 1; kasvattaa muuttujan ika arvoa yhdellä. Käytännössä komennon suoritus tapahtuu siten, että ensin suoritetaan yhtäsuuruusmerkin oikealla puolella oleva lauseke ika + 1. Tässä tietokone etsii ensin muuttujaa ika. Koska muuttuja on esitelty aiemmin, se löytyy (sen arvo on 1). Lausekkeen ika + 1 muuttujan ika paikalle asetetaan arvo 1. Lauseke on nyt muotoa 1 + 1, jonka tietokone osaa laskea: tulokseksi tulee 2. Lause on tämän jälkeen kokonaisuudessaan ika = 2, mikä tarkoittaa "aseta muuttujan ika arvoksi 2".

Muuttujan arvon kasvattaminen yhdellä onnistuu myös seuraavasti:

int ika = 1;

System.out.println(ika);
ika++;                     // tarkoittaa samaa kuin ika = ika + 1;
System.out.println(ika);

Lause ika++ muuntuu ohjelmaa suoritettaessa muotoon ika = ika + 1;. Ohjelman tulostus on sama kuin edellisessä:

1
2

Toinen esimerkki:

int pituus = 100;

System.out.println(pituus);
pituus = pituus - 50;
System.out.println(pituus);
pituus = pituus * 2;
System.out.println(pituus);
pituus = pituus / 4;
System.out.println(pituus);
pituus--;                   // sama kuin pituus = pituus - 1;
System.out.println(pituus);
100
50
100
25
24

Muuttujan olemassaolo

Muuttujan esittely ei takaa sen olemassaoloa kaikkialla ohjelmassa. Muuttujan olemassaoloon liittyy oleellisesti kaksi sääntöä: muuttujaa ei ole olemassa ennen kuin se on esitelty, ja muuttuja ei ole olemassa aaltosulkeidensa ulkopuolella.

Muuttujan olemassaolo liittyy muuttujan esittelyyn

Muuttuja on olemassa vasta siitä lähtien kun muuttuja esitellään, eikä sitä voi käyttää ennen kuin se on esitelty. Muuttujan esittely varaa ohjelman muistista muuttujalle tilan, asettaa tilalle nimen, sekä -- jos asetuslausetta käytetään -- asettaa muuttujalle arvon. Tämä nimetty tila on olemassa vasta muuttujan esittelyn jälkeen.

// ei toimi sillä muuttujaa luku ei vielä tunneta!
if (luku > 10) {
    System.out.println("Jee!");
}

int luku = 12;
int luku = 12;

// toimii, sillä muuttuja esitellään ennen sen käyttöä
if (luku > 10) {
    System.out.println("Jee!");
}
Jee!

Muuttujan olemassaolo liittyy aaltosulkeisiin

Muuttuja on olemassa vain niiden aaltosulkujen sisällä missä muuttuja on esitelty, eikä muuttujaan pääse käsiksi aaltosulkujen ulkopuolelta. Esimerkiksi, jos ehtolauseeseen liittyvässä lohkossa (aaltosuluilla rajattu alue) esitellään muuttuja, on kyseinen muuttuja on käytössä vain kyseisen lohkon sisällä.

int luku = 12;

if (luku > 10) {
    System.out.println("Jee!");
    int tulostus = 52;
}

// ei toimi sillä tulostus on olemassa vain ehtolauseen lohkojen sisällä
System.out.println(tulostus);
int luku = 12;
int tulostus = 0;

if (luku > 10) {
    System.out.println("Jee!");
    tulostus = 52;
}

System.out.println(tulostus); // toimii!
Jee!
52

Toisen säännön yhteydessä myös ensimmäinen sääntö pätee. Muuttuja on olemassa vain sen esittelyhetkestä eteenpäin, ei koskaan ennen esittelyhetkeä.

Kaverisi on hahmotellut summaamiseen ja ehtolauseen toimintaan liittyvää testiohjelmaa. Testiohjelmassa ohjelman alussa luettuun lukuun lisätään käyttäjältä luettu toinen luku. Luvun lisäämisen tulee tapahtua vain jos toisena luettu luku on positiivinen.

Ohjelma ei tällä hetkellä kuitenkaan toimi ihan toivotusti ja siinä näkyy punaista. Kaverisi ohjelma näyttää seuraavalta:

Scanner lukija = new Scanner(System.in);
int summa = Integer.parseInt(lukija.nextLine());

if (luettu > 0) {
    summa += summa + luettu;
}

int luettu = Integer.parseInt(lukija.nextLine());

System.out.println("Summa: " + summa);

Korjaa kaverisi ohjelma. Alla esimerkkitulosteita toimivasta ohjelmasta.

32
7
Summa: 39
9
-3
Summa: 9

Työntekijän eläkevakuutus (TyEL) on lakisääteinen ja pakollinen maksu, jolla rahoitetaan suurin osa nykyisistä ja tulevista työeläkkeistä. Työntekijän eläkevakuutuksen maksamisesta vastaa sekä työnantaja että työntekijä: Eläketurvakeskuksen mukaan työnantajan maksama TyEL-maksu on vuonna 2017 noin 18% työntekijän palkasta, kun taas työntekijä maksaa palkastaan TyEL-maksua noin 6%.

Työnantaja maksaa osuutensa bruttopalkan lisäksi, kun taas työntekijän maksama osuus otetaan bruttopalkasta (eli palkasta ennen ennakonpidätyksiä, vakuutusmaksuja ja muita vähennyksiä).

Toteuta ohjelma, joka kertoo TyEL-kulut annetulle bruttopalkalle. Ohjelman tulee kertoa annetun bruttopalkan perusteella työnantajan maksut sekä palkasta tehtävät pidätykset. Käytä edellä annettuja vuoden 2017 arvioita (työnantajan osuus 18% ja työntekijän osuus 6%).

Syötä bruttopalkka: 800
Työnantaja maksaa TyEL-maksuja: 144
Työntekijän kustannus työnantajalle vähintään: 944

Työntekijä maksaa TyEL-maksuja: 48
Työntekijän palkka TyEL-maksun jälkeen: 752

TyEL-kulut yhteensä: 192
Syötä bruttopalkka: 1000
Työnantaja maksaa TyEL-maksuja: 180
Työntekijän kustannus työnantajalle vähintään: 1180

Työntekijä maksaa TyEL-maksuja: 60
Työntekijän palkka TyEL-maksun jälkeen: 940

TyEL-kulut yhteensä: 240

Tulostuksessa saa halutessaan käyttää myös desimaalilukuja.

Sijoitusoperaatiot

Olemassaolevan muuttujan arvon muuttaminen on yleinen operaatio, joten sitä varten on olemassa myös muutamia näppäinpainalluksia säästävät erityiset sijoitusoperaatiot.

int pituus = 100;

pituus += 10;  // sama kuin pituus = pituus + 10;
pituus -= 50;  // sama kuin pituus = pituus - 50;

Olemassaolevan muuttujan arvoa muuttava sijoitusoperaatio merkitään muuttuja muutostyyppi= muutos, esimerkiksi muuttuja += 5. Huomaa, että muuttujan tulee olla olemassa ennen kuin sille voidaan asettaa arvo. Muuttuja tulee siis aina esitellä ennen kuin se on käytettävissä. Muuttujan esittely tapahtuu kertomalla muuttujan tyyppi ja nimi.

Seuraava esimerkki ei toimi, sillä muuttujaa leveys ei ole esitelty.

leveys += 100;           // ei toimi!

Kun muuttuja on esitelty, laskukin toimii oikein.

int leveys = 100;  // muuttujan esittely ja arvon asetus
leveys += 100;     // toimii!

Myös muille kuin yhteen- ja vähennyslaskuille on Javassa vastaavat sijoitusoperaatiot.

int pituus = 100;
System.out.println(pituus);

pituus *= 10; // sama kuin pituus = pituus * 10;
System.out.println(pituus);

pituus /= 100; // sama kuin pituus = pituus / 100;
System.out.println(pituus);

pituus %= 3; // sama kuin pituus = pituus % 3;
System.out.println(pituus);
100
1000
10
1

Syötteen lukeminen ja sijoitus

Käyttäjän kirjoittamaa syötettä voidaan lukea Scanner-apuvälineen avulla:

Scanner lukija = new Scanner(System.in);
int pituus = 100;

System.out.println(pituus);

System.out.print("Paljonko poistetaan? ");
pituus = pituus - Integer.parseInt(lukija.nextLine());
System.out.println(pituus);

System.out.print("Paljonko lisätään? ");
pituus = pituus + Integer.parseInt(lukija.nextLine());
System.out.println(pituus);

Tulostus esimerkiksi:

100
Paljonko poistetaan? 42
58
Paljonko lisätään? 1
59

Tarkastellaan lauseen pituus = pituus - Integer.parseInt(lukija.nextLine()); suoritusta tarkemmin.

Kyseessä on sijoituslause, missä muuttujaan pituus asetetaan lausekkeen pituus - Integer.parseInt(lukija.nextLine()) evaluaation tuottama arvo. Lauseke pituus - Integer.parseInt(lukija.nextLine()) evaluoidaan seuraavasti:

  1. Tietokone etsii muuttujan pituus arvon. Koska muuttuja on luotu aiemmin, se löytyy: sen arvo on 100. Nyt lauseke on muotoa 100 - Integer.parseInt(lukija.nextLine());.
  2. Tietokone suorittaa lausekkeen osan Integer.parseInt(lukija.nextLine()):
    1. Ensin suoritetaan sulkujen sisällä oleva osa, eli lukija.nextLine(). Tämä johtaa siihen, että ohjelma jää odottamaan käyttäjältä syötettä. Kun käyttäjä antaa syötteen, esimerkiksi "42", vaihtuu komennon lukija.nextLine() paikalle käyttäjän syöttämä merkkijono "42". Seuraavaksi suoritettava komento on nyt Integer.parseInt("42");.
    2. Integer.parseInt("42") muuntaa tekstimuuttujan, jonka arvo on "42", kokonaislukumuuttujaksi, jonka arvo on 42.
  3. Lausekkeen Integer.parseInt(lukija.nextLine()) paikalle asetetaan sen evaluaatiosta saatu arvo, tässä 42. Nyt lauseke on muotoa 100 - 42.
  4. Tietokone evaluoi lausekkeen 100 - 42, jonka tulos on 58.

Lopulta lause on muotoa pituus = 58, eli muuttujan pituus arvoksi asetetaan 58.

Tee ohjelma, joka kysyy käyttäjältä kolme lukua ja tulostaa niiden summan. Huom! Käytä tehtäväpohjaan valmiiksi luotuja muuttujia -- älä siis luo uusia muuttujia koodiin. Tee ohjelmastasi seuraavan muotoinen:

Scanner lukija = new Scanner(System.in);
int summa = 0;

// KIRJOITA OHJELMA TÄHÄN
// ÄLÄ KÄYTÄ MUITA MUUTTUJIA KUIN lukija JA summa!

System.out.println("Summa: " + summa);
Anna ensimmäinen luku: 3
Anna toinen luku: 6
Anna kolmas luku: 12

Summa: 21

Uusi ystäväsi: Crowdsorcerer

Ohjelmoidessa tarkkaa tehtävänantoa ei yleensä ole, vaan pikemminkin jonkinlainen haluttu lopputulos. Seuraavaksi onkin tarkoitus opetella kehittämään tehtävänantoja itse. Tehtävien luominen auttaa käsittämään prosessia koodaamisen taustalla: onko olennaista toteuttaa tehtävä vaikkapa ehtolauseita käyttäen, vai olisiko jokin muu lähestymistapa parempi. Näiden tehtävänantojen tekeminen auttaa myös kurssin sisältöjen omaksumisessa ja kokonaisuuksien hahmottamisessa.

Koodarit toimivat usein yhdessä, joten on hyödyllistä osata lukea toisten koodia. Myös omat ohjelmointitaidot paranevat, kun oppii huomaamaan ja välttämään yleisiä ohjelmointivirheitä. Ohjelman koodi on myös osa sen dokumentaatiota, eli koodia lukemalla oppii ymmärtämään erilaisten ohjelmien toimintaa.

Nyt sinulla on myös mahdollisuus saada kurssille juuri sinua kiinnostavia tehtäviä! Alla olevan vempaimen, Crowdsorcererin, avulla voit luoda tehtävänantoja malliratkaisuineen muille kurssilaisille.

Crowdsorcererin käyttö

Keksi ohjetta vastaava tehtävä. Tehtävän aiheena voi olla vaikkapa syötteen tulostus tai toistolauseen käyttö. Kirjoita selkeä tehtävänanto, jossa kerrot lyhyesti toteutettavan ohjelman perusidean ja esimerkiksi käytettävät muuttujat. Halutessasi voit ottaa mallia kurssin tehtävänannoista. Tarkoituksena on kuvata ongelma tarpeeksi tarkasti, jotta tehtävän tekijä osaa koodata sille ratkaisun.

Kirjoita tämän jälkeen tehtävälle malliratkaisu. Lähdekoodikentässä on valmiiksi annettuna koodipohja, jossa harmaalla taustalla merkittynä ovat rivit, joita et voi muuttaa. Osa koodista, niin kutsuttu tehtäväpohja, tulee tehtävän tekijälle näkyviin. Painamalla rivinumeroiden vieressä olevia neliöitä voit merkitä osan tehtävästä malliratkaisuksi, jolloin nuo rivit eivät näy tehtävän tekijälle. Tehtäväpohjaksi tulee ne rivit, joita et merkitse siniseksi. Painamalla "Nollaa malliratkaisu" -nappia saat palautettua malliratkaisun sen alkuperäiseen tilaan.

Toteuttamasi malliratkaisun toimivuus tarkistetaan testien avulla. Sinä määrität, miten malliratkaisusi pitäisi toimia tietyssä tilanteessa, testit toteuttavat tämän tilanteen, ja kertovat, toimiko ohjelma kuten piti. Hyvin usein ohjelmat toimivat niin, että käyttäjältä pyydetään jotain tietoa, jota ohjelma prosessoi, minkä jälkeen ohjelma palauttaa käyttäjälle uutta tietoa. Tällöin syötteellä tarkoitetaan käyttäjältä pyydettyä tietoa, ja tuloksella ohjelman palauttamaa tietoa. Tehdessäsi tehtävää, kirjoita malliratkaisun testeille syöte ja odotettu tulos niille varattuihin kenttiin. Kun lähetät tehtävän, malliratkaisua testataan antamallasi syötteellä, ja ohjelman antamaa tulosta verrataan määrittämääsi tulokseen. Jos ohjelman tuloste ei sisällä määrittämääsi tulosta, kertoo Crowdsorcerer, miten tulokset erosivat toisistaan. Syöte-tuloste-pareja on oltava vähintään yksi, mutta kannattaa keksiä useampia.

Anna lopuksi tehtävälle kuvaavat tagit. Vempain ehdottaa usein käytettyjä tageja, mutta voit myös keksiä omasi.

Kun lähetät tehtävän, se välitetään palvelimelle, joka tarkastaa tehtävän ja malliratkaisun toimivuuden. Tämä prosessi vie hieman aikaa, joten odotathan kärsivällisesti! Mikäli tehtävässäsi on ongelmia, saat palautteena virheviestin korjaamisen avuksi. Voit lähettää tehtävän uudelleen niin monta kertaa kuin haluat, ja viimeisin hyväksytysti läpimennyt tehtävä annetaan muiden opiskelijoiden arvioitavaksi seuraavalla viikolla.

Suunnittele oma tehtävä: Ehtolause
Tee tehtävä, jonka tarkoitus on laittaa opiskelija koodaamaan ohjelma, joka lukee käyttäjältä kokonaislukusyötteen, tarkastelee sitä ehtolauseen avulla ja tulostaa merkkijonon. Anna testejä vasten syöte-esimerkki ja ohjelman tuloste tuolla syötteellä.

Toistolause

Tutustuimme ensimmäisessä osassa pikaisesti toistolauseeseen. Toistetaan asiaa hieman ja syvennytään toistolauseen toimintaan tarkemmin.

Toistolause while (true) { /* toistettava */ } saa aikaan sen, että toistolauseeseen liittyvää lohkoa, eli {}:lla rajattuja komentoja suoritetaan monta kertaa. Lohkossa olevien rivien määrää ei ole rajattu.

Toistolauseesta poistuminen

Toistolauseesta poistuminen onnistuu break-komennolla, joka kirjoitetaan tyypillisesti toistolauseen lohkon sisällä olevaan ehtolauseeseen, missä tarkastellaan haluaako käyttäjä poistua toistolauseesta.

Alla olevassa esimerkissä toistolauseen avulla tulostetaan luvut viidestä yhteen.

int luku = 5;

while (true) {
    System.out.println(luku);
    luku--;

    if(luku < 1) {
        break;
    }
}
5
4
3
2
1
Toistolauseesta poistutaan vasta kun break-komento suoritetaan

Huomaa, että toistolauseesta poistutaan kun ohjelman suoritus saavuttaa break-komennon. Alla edellä olevaan esimerkkiin on tehty näennäisesti pieni muutos, mikä samalla muuttaa tulostusta.

int luku = 5;

while (true) {
    System.out.println(luku);

    if(luku < 1) {
        break;
    }

    luku--;
}
5
4
3
2
1
0

Tee ohjelma, joka tulostaa kokonaisluvut väliltä 1–100.

Ohjelman tulostus on seuraava:

1
2
3
(välissä paljon rivejä)
98
99
100

Tee ohjelma, joka tulostaa kokonaisluvut väliltä 100–1.

Ohjelman tulostus on seuraava:

100
99
98
(välissä paljon rivejä)
3
2
1

Tee ohjelma, joka tulostaa parilliset kokonaisluvut väliltä 2–100.

2
4
6
(välissä paljon rivejä)
96
98
100

Toistolause ja interaktiivinen toiminnallisuus

Interaktiivisen toiminnallisuuden lisääminen toistolauseeseen onnistuu suoraviivaisesti. Alla olevassa esimerkissä on toteutettu ohjelma, joka lukee käyttäjältä lukuja. Kun käyttäjä syöttää luvun 0, ohjelman suoritus päätyy break-komentoon ja lukeminen lopetetaan.

Scanner lukija = new Scanner(System.in);

while (true) {
    System.out.println("osaan ohjelmoida!");

    System.out.print("jatketaanko (0 lopettaa)? ");
    int komento = Integer.parseInt(lukija.nextLine());
    if (komento == 0) {
        break;
    }
}

System.out.println("kiitos ja kuulemiin.");

Kun tietokone suorittaa komennon break, siirtyy se toistolausetta seuraavan komennon suorittamiseen.

Hieman yleistäen, toistolausetta käyttävän ohjelman rakenne on tyypillisesti seuraavanlainen.

// lukijan esittely
Scanner lukija = new Scanner(System.in);
// tarvittavien muuttujien esittely

while (true) {
    // lukeminen ja lopetusehdon tarkistaminen
    int luettu = Integer.parseInt(lukija.nextLine());
    if (luettu == 0) {
        break;
    }

    // toiminnallisuus, joka tehdään jos toistoa
    // ei vielä lopeteta
}

// toiminnallisuus, joka tehdään toiston
// lopettamisen jälkeen

Tee ohjelma, joka lukee käyttäjältä lukuja ja tulostaa niiden summan. Ohjelma lopettaa kyselemisen kun syötetään luku 0. Käytä seuraavaa pohjaa, jonka saat myös palautusautomaatilta:

Scanner lukija = new Scanner(System.in);

int summa = 0;
System.out.print("Anna lukuja, nolla lopettaa: ");
while (true) {
    int luettu = Integer.parseInt(lukija.nextLine());
    if (luettu == 0) {
        break;
    }

    // TEE JOTAIN TÄÄLLÄ

}

System.out.println("Summa lopussa: " + summa);

Ohjelman tulee toimia seuraavasti:

Anna lukuja, nolla lopettaa:
3
2
1
1
0
Summa: 7

Kirjoita ohjelma, joka kysyy käyttäjältä lukua. Jos luku on positiivinen, tulostetaan "Juupas", jonka jälkeen kysytään lukua uudestaan. Jos luku on negatiivinen, tulostetaan "Eipäs" ja kysytään lukua uudestaan. Ohjelman suoritus loppuu kun käyttäjä syöttää luvun 0.

Kun mietit minkälaisilla syötteillä ohjelma toimii toivotusti, erilaisia toimintoja on 3. Ohjelmaa kannattaa siis testata ainakin kolmella erilaisella syötteellä, jonka lisäksi lienee hyvä tarkastaa ohjelman toiminta hyvin pienillä (esim. -10000) ja hyvin suurilla syötteillä (10000).

Alla vielä toivottu toiminnallisuus vuokaaviona.

st=>start: Alku

						io=>inputoutput: lue luku

						cond=>condition: luku > 0
						cond2=>condition: luku < 0

						io2=>inputoutput: "Juupas"
						io3=>inputoutput: "Eipäs"
						e=>end: Loppu

						st->io->cond
						cond(yes, right)->io2->io
						cond(no)->cond2
						cond2(yes, right)->io3->io
						cond2(no)->e

Esimerkki: Laskin

Seuraavassa esimerkissä on toteutettu summa- ja erotustoiminnallisuudet tarjoava laskin. Laskin kysyy käyttäjältä numeerista komentoa. Komennolla 0 poistutaan toistolauseesta. Jos komento ei ole lopetus, kysytään kahta lukua. Jos komento oli 1 lasketaan lukujen summa ja tulostetaan se. Jos komento oli 2 lasketaan lukujen erotus ja tulostetaan se. Muussa tapauksessa ilmoitetaan että komento on tuntematon. Lopulta -- kun suoritus päätyy toistolauseen lohkon päättävään aaltosulkuun -- palataan toistolauseen lohkon alkuun -- eli toistolauseen lohkon avaavaan aaltosulkuun --, mistä ohjelman suoritus jatkuu.

System.out.println("tervetuloa käyttämään laskinta");

while (true) {
    System.out.print("anna komento (1=summa, 2=erotus, 0=lopetus): ");

    int komento = Integer.parseInt(lukija.nextLine());
    if (komento == 0) {
        break;
    }

    System.out.print("anna luvut ");
    int eka = Integer.parseInt(lukija.nextLine());
    int toka = Integer.parseInt(lukija.nextLine());

    if (komento == 1) {
        int summa = eka + toka;
        System.out.println("lukujen summa " + summa);
    } else if (komento == 2) {
        int erotus = eka - toka;
        System.out.println("lukujen erotus " + erotus);
    } else {
        System.out.println("tuntematon komento");
    }
}

System.out.println("kiitos ja kuulemiin.");

Toiston jatkaminen

Törmäämme usein tilanteeseen, missä osa ohjelmalle annetuista syötteistä on epäkelpoja. Jos syötteitä käsitellään toistolauseessa, haluamme mahdollisimman nopeasti palata uuden syötteen lukemiseen. Komennolla continue siirrytään toistolauseen alkuun.

Scanner lukija = new Scanner(System.in);

System.out.print("Anna lukuja, negatiiviset luvut eivät kelpaa: ");

while (true) {
    int luettu = Integer.parseInt(lukija.nextLine());

    // tarkastetaan onko syöte epäkelpo -- jos kyllä, palataan toistolauseen
    // alkuun
    if (luettu < 0) {
        continue;
    }

    // tehdään jotain täällä jos syöte on kelpo eli nolla tai positiivinen
}

Ylläoleva ohjelma palaa heti uuden syötteen lukemiseen jos käyttäjä syöttää negatiivisen luvun.

Komentoja break ja continue käytetään usein yhdessä. Komennolla break lopetetaan toistolauseen suoritus, kun taas komennolla continue voidaan rajata vain osa syötteistä käsiteltäväksi.

Allaoleva ohjelma laskee positiivisten syötteiden summan. Jos käyttäjä syöttää negatiivisen luvun, ohjelma pyytää käyttäjältä seuraavaa lukua. Toistolauseesta poistutaan jos käyttäjä syöttää nollan.

Scanner lukija = new Scanner(System.in);

System.out.print("Anna lukuja, negatiiviset luvut eivät kelpaa: ");
int summa = 0;

while (true) {
    int luettu = Integer.parseInt(lukija.nextLine());

    if (luettu == 0) {
        break;
    }

    if (luettu < 0) {
        continue;
    }

    summa += luettu;
}

System.out.println("Hyväksyttävien lukujen summa: " + summa);

Ohjelmaan voi tuoda mukaan myös muita muuttujia. Allaolevassa esimerkissä lasketaan summan lisäksi hyväksyttyjen ja epäkelpojen lukujen lukumäärä.

Scanner lukija = new Scanner(System.in);

System.out.print("Anna lukuja, negatiiviset luvut eivät kelpaa: ");
int summa = 0;
int hyvaksytytLuvut = 0;
int epakelvotLuvut = 0;

while (true) {
    int luettu = Integer.parseInt(lukija.nextLine());

    if (luettu == 0) {
        break;
    }

    if (luettu < 0) {
        epakelvotLuvut++;
        continue;
    }

    summa += luettu;
    hyvaksytytLuvut++;
}

System.out.println("Hyväksyttävien lukujen summa: " + summa);
System.out.println("Hyväksyttyjä lukuja: " + hyvaksytytLuvut);
System.out.println("Epäkelvot luvut: " + epakelvotLuvut);

Toteuta ohjelma, joka lukee käyttäjän antamia syötteitä ja laskee kelvollisten lukujen lukumäärän. Luku on kelvollinen, jos se on suurempi tai yhtäsuuri kuin -140 ja pienempi tai yhtäsuuri kuin 20. Lopeta syötteiden lukeminen kun käyttäjä syöttää luvun 9999.

Jos käyttäjä syöttää luvun, joka on pienempi kuin -140 tai suurempi kuin 20, hänelle kerrotaan ettei syötetty luku ollut kelvollinen.

Syötä luku: 5
Syötä luku: 22
Kelvoton luku
Syötä luku: -11
Syötä luku: -140
Syötä luku: -18
Syötä luku: 9999

Kelvollisia lukuja yhteensä: 4

Toteuta ohjelma, joka lukee käyttäjän antamia syötteitä ja laskee kelvollisten lukujen summan. Luku on kelvollinen, jos se on suurempi tai yhtäsuuri kuin -140 ja pienempi tai yhtäsuuri kuin 20. Lopeta syötteiden lukeminen kun käyttäjä syöttää luvun 9999.

Jos käyttäjä syöttää luvun, joka on pienempi kuin -140 tai suurempi kuin 20, hänelle kerrotaan ettei syötetty luku ollut kelvollinen.

Syötä luku: 5
Syötä luku: 22
Kelvoton luku
Syötä luku: -11
Syötä luku: -140
Syötä luku: -18
Syötä luku: 9999

Kelvollisten lukujen summa: −164

Toteuta ohjelma Mars-planeetan lämpötilamittausten tarkasteluun. Marsin alin lämpötila (pinnalla) on -140 astetta, korkein lämpötila on 20 astetta. Ohjelmalle syötetään mittauksia kunnes käyttäjä syöttää luvun 9999. Tämän jälkeen kerrotaan lämpötilamittausten keskiarvo.

Jos et muista miten keskiarvo lasketaan, tutustu aiheen Wikipedia-sivuun.

Jos käyttäjä syöttää luvun, joka on pienempi kuin -140 tai suurempi kuin 20, lukua ei huomioida lämpötilan keskiarvon laskemisessa.

Syötä mittaus: -41
Syötä mittaus: -11
Syötä mittaus: 23
Syötä mittaus: 2
Syötä mittaus: -14
Syötä mittaus: -22
Syötä mittaus: -45
Syötä mittaus: 9999

Mittausten keskiarvo: -21.833333333333332

Toteuta ohjelma, missä käyttäjän tulee arvata välillä 1-10 oleva luku. Aseta pelin arvattavaksi luvuksi 7.

Ohjelman suoritus loppuu kun käyttäjä arvaa oikean luvun (eli 7). Jos käyttäjä syöttää luvun, joka on suurempi kuin 10 tai pienempi kuin 1, käyttäjälle kerrotaan ettei hänen syöttämänsä luku ollut kelvollinen.

Ohjelman tulee kertoa käyttäjälle käyttäjän tekemien kelvollisten arvausten määrä. Välille 1-10 osuvat arvaukset lasketaan kelvollisiksi, mukaan lukien oikea arvaus.

Minäpä tiedän luvun väliltä 1-10, jota sinä et tiedä!

Arvaa luku: 5
Ei ollut!
Arvaa luku: 28
Epäkelpo luku!
Arvaa luku: 10
Ei ollut!
Arvaa luku: 7

Oikein! Arvauksia yhteensä: 3

Kun saat ohjelman valmiiksi, palauta se. Voit halutessasi tämän jälkeen kokeilla pelin pelaamista myös satunnaisilla luvuilla. Välillä 1-10 olevan satunnaisen luvun arpominen onnistuu seuraavasti:

int luku = (int) (1 + Math.random() * 10);

Toteuta ohjelma, missä käyttäjän tulee arvata välillä 1-100 oleva luku. Aseta pelin arvattavaksi luvuksi 42.

Ohjelman suoritus loppuu kun käyttäjä arvaa oikean luvun (eli 42). Jos käyttäjä syöttää luvun, joka on suurempi kuin 100 tai pienempi kuin 1, käyttäjälle kerrotaan ettei hänen syöttämänsä luku ollut kelvollinen.

Jos käyttäjä syöttää luvun, joka on pienempi kuin arvattava luku, käyttäjälle kerrotaan että arvattava luku on suurempi. Jos taas käyttäjä syöttää luvun, joka on suurempi kuin arvattava luku, käyttäjälle kerrotaan että arvattava luku on pienempi.

Ohjelman tulee kertoa käyttäjälle myös tehtyjen kelvollisten arvausten määrä.

Minäpä tiedän luvun väliltä 1-100, jota sinä et tiedä!

Arvaa luku: 5
Lukuni on isompi!
Arvaa luku: 25
Lukuni on isompi!
Arvaa luku: 450
Epäkelpo luku!
Arvaa luku: 50
Lukuni on pienempi!
Arvaa luku: 45
Lukuni on pienempi!
Arvaa luku: 40
Lukuni on isompi!
Arvaa luku: 42

Oikein! Arvauksia yhteensä: 6

Kun saat ohjelman valmiiksi, palauta se. Voit halutessasi tämän jälkeen kokeilla pelin pelaamista myös satunnaisilla luvuilla. Välillä 1-100 olevan satunnaisen luvun arpominen onnistuu seuraavasti:

int luku = (int) (1 + Math.random() * 100);

Toistolauseen ehtolauseke

Olemme tähän mennessä käyttäneet toistolausetta, jonka ehtolausekkeen arvona on true. Tämä tarkoittaa sitä, että toistoa jatketaan ikuisesti.

Ehtolausekkeeseen voi asettaa myös lausekkeen, joka evaluoidaan ohjelman suorituksen yhteydessä. Lauseke määritellään täsmälleen samalla tavalla kuin ehtolauseen (if) ehto.

Seuraavassa esimerkissä tulostetaan luvut 1, 2, ..., 5. Kun luku-muuttujan arvo on yli 5, while-ehto ei ole enää voimassa ja toistaminen lopetetaan.

int luku = 1;

while (luku < 6) {
    System.out.println(luku);
    luku++;
}

Lue ylläoleva "niin pitkään kuin muuttujan luku arvo on pienempi kuin 6, tulosta muuttujan luku arvo ja kasvata muuttujan luku arvoa yhdellä".

Yllä muuttujan luku arvoa kasvatetaan yhdellä aina kun toistolauseen lohko suoritetaan.

int luku = 1024;

while (luku >= 1) {
    System.out.println(luku);
    luku /= 2;
}

Screencast aiheesta:

Moniosaisista tehtävistä

Huomaa, että tästä lähtien tehtävissä saattaa olla useampia osia. Jokainen osa lasketaan yksittäiseksi tehtäväksi, eli esimerkiksi seuraava tehtävä vastaa kahta yksiosaista tehtävää. Useampiosaiset tehtävät voi tyypillisesti palauttaa myös vaikka tehtävä ei olisi vielä valmis -- tällöin valmiista osista lisätään pisteet kirjanpitoon.

Tämä tehtävä on ensimmäinen kaksiosainen tehtävä. Kun teet molemmat osat, saat tehtävästä kaksi tehtäväpistettä. Voit palauttaa tehtävän myös siten, että vain ensimmäinen osa on tehtynä.

Mihin asti?

Kirjoita ohjelma, joka tulostaa kokonaisluvut 1:stä käyttäjän antamaan lukuun asti.

Mihin asti? 3
1
2
3
Mihin asti? 5
1
2
3
4
5

Vihje: käyttäjältä lukemasi luku toimii nyt whilen lopetusehdon ylärajana. Muista että Javassa a <= b tarkoittaa a pienempi tai yhtä suuri kuin b.

Mistä lähtien?

Lisää ohjelmaan käyttäjältä kysyttävä alaraja.

Mihin asti? 8
Mistä lähtien? 5
5
6
7
8

Jos tavoite on suurempi kuin lähtökohta ei tulostu mitään:

Mihin asti? 12
Mistä lähtien? 16

Huom! muista että ala- ja yläraja voivat olla myös negatiivisia!

Toistolauseke ja toiston suorituksen lopettamisesta

Toistolauseen suoritus ei lopu heti kun toistolauseen ehtolauseke voisi evaluoitua todeksi. Toistolauseen ehtolauseke evaluoidaan aina kun saavutaan toistolauseen alkuun, eli (1) kun ohjelman seuraava suoritettava lause on toistolause, ja (2) kun toistolauseeseen liittyvän lohkon sisältämän ohjelmakoodin suoritus on saatu loppuun.

Tarkastellaan seuraavaa toistolausetta.

int luku = 1;

while (luku != 2) {
    System.out.println(luku);
    luku = 2;
    System.out.println(luku);
    luku = 1;
}

Ohjelman tulostus seuraavanlainen:

1
2
1
2
1
2
...

Vaikka muuttujan luku arvo on välillä 2, toistolauseen suoritus ei lopu koskaan.

Toistolauseen ehto tarkistetaan siis vain kun toistolauseen toistaminen aloitetaan sekä silloin kun koodin suoritus on päässyt toistolauseen lopettavaan aaltosulkuun asti. Jos ehto on totta, suoritus hyppää takaisin toistolauseen alkuun, ja jos ehto on epätotta, suoritus siirtyy toistolausetta seuraavaan lauseeseen.

Vaikka muuttujan luku arvo on ylläolevassa toistolauseessa välillä 2, ei se ole koskaan 2 toistolauseen lopussa. Lopussa ehto luku != 2 on aina totta, ja suoritus jatkuu..

Toisto ja useampi muuttuja

Ohjelmat ovat usein tyyppiä "tee jotain tietty määrä kertoja". Näissä ohjelmissa esiintyy toisto, jonka jokaisella toistokerralla tehdään haluttu toiminnallisuus sekä muutetaan kertojen lukumäärää laskevaa laskurimuuttujaa.

Seuraava ohjelma laskee tulon 4*3 hieman kömpelöllä tavalla eli summana 3 + 3 + 3 + 3:

int tulos = 0;

int i = 0;
while (true) {
    tulos += 3; // tarkoittaa samaa kuin tulos = tulos + 3;
    i++;  // tarkoittaa samaa kuin i = i + 1;

    if (i == 4) {
         break;
    }
}

System.out.println(tulos);

Saman toiminnallisuuden voi toteuttaa myös seuraavasti.

int tulos = 0;

int i = 0;
while (i < 4) {
    tulos += 3; // tarkoittaa samaa kuin tulos = tulos + 3;
    i++;  // tarkoittaa samaa kuin i = i + 1;
}

System.out.println(tulos);

Mitä enemmän ohjelmassa on muuttujia, sitä haastavampaa ohjelman askeleittaisen suorituksen seuraaminen on. Ohjelman ymmärtämisen kannalta suorituksen seuraaminen on kuitenkin tärkeää.

Yksi näppärä tapa muuttujien arvojen tarkasteluun toistolauseessa on taulukko. Seuraavaan taulukkoon on kirjoitettu auki edellisen esimerkin muuttujien tulos ja i arvot kullakin toistolauseen ehdon i < 4 vertailuhetkellä.

tulos i i < 4
0 0 true
3 1 true
6 2 true
9 3 true
12 4 false

Toistolauseen suoritus loppuu kun muuttujan summa arvo on 12 ja muuttujan i arvo on 4 (ehto i < 4 on tällöin epätotta).

Tee ohjelma, joka laskee summan 1+2+3+...+n, missä n on käyttäjän syöttämä luku.

Esimerkkitulostuksia:

Mihin asti? 3
Summa on 6

Edellisessä esimerkissä laskettiin 1 + 2 + 3 = 6

Mihin asti? 7
Summa on 28

Ja nyt laskettiin 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28

Vihje: Tee ohjelma while-komennon avulla. Käytä ohjelmassasi apumuuttujaa toistokertojen muistamiseen. Lisää jokaisella toistokerralla toistokerrat muistavan muuttujan arvo apumuuttujaan johon lasket summan arvon.

Muuta edellistä tehtävää siten, että käyttäjä määrää summan laskemisen aloituskohdan. Voit olettaa, että käyttäjä antaa ensin pienemmän luvun ja sitten suuremman luvun.

Esimerkkitulostuksia:

Ensimmäinen: 3
Viimeinen: 5
Summa on 12

Edellisessä laskettiin 3 + 4 + 5 = 12

Ensimmäinen: 2
Viimeinen: 8
Summa on 35

Ja nyt laskettiin 2 + 3 + 4 + 5 + 6 + 7 + 8 = 35

Tee ohjelma, joka laskee käyttäjän syöttämän luvun kertoman.

Kertoma n! lasketaan kaavalla 1*2*3*...*n. Esimerkiksi luvun 4 kertoma on 24, eli 4! = 1*2*3*4 = 24. Lisäksi on määritelty, että luvun 0 kertoma on 1, eli 0! = 1.

Esimerkkitulostuksia:

Anna luku: 3
Kertoma on 6

Nyt laskettiin 1 * 2 * 3 = 6

Anna luku: 10
Kertoma on 3628800

Ja nyt laskettiin 1 * 2 * 3 * ... * 8 * 9 * 10 = 3628800

Lisätietoa: Kertomaa käytetään erityisesti todennäköisyyslaskennassa tilanteissa, missä halutaan esimerkiksi tarkastella jonkin joukon kaikkia erilaisia järjestyksiä. Esimerkiksi viiden hengen ryhmän voi järjestää 5! erilaiseen jonoon, ja 52 kortin korttipakka voi olla 52! erilaisessa järjestyksessä. Kertomaa voi käyttää myös kombinaatioiden laskemiseen; esimerkiksi 52 kortin korttipakasta on mahdollista jakaa 52! / (5! * (52 - 5)!) erilaisella viiden kortin kättä, ja 40 numeron joukosta voi tehdä yhteensä 40! / (7! * (40 - 7)!) erilaista 7 numeron lottoriviä.

Kaverisi on järjestämässä pienimuotoiset kekkerit. Hän on hyvin tarkka plaseerauksen suhteen ja miettii miten vieraat pitäisi asettaa pöytään.

Auta toteuttamasi ohjelman avulla kaveriasi ymmärtämään, ettei kaikkia mahdollisia plaseerausvaihtoehtoja kannata käydä läpi pohtia.

Ohjelmaan on toteutettuna valmiiksi graafinen käyttöliittymä, mihin kaverisi voi syöttää vieraiden lukumäärän. Muuta ohjelman toimintaa siten, että ohjelma laskee plaseerausvaihtoehtojen lukumäärän, ja näyttää sen käyttäjälle. Hyödynnä tässä edellisessä tehtävässä tekemääsi kertomaa.

 

 

Huomaat todennäköisesti, että ohjelma toimii vain melko pienillä vieraslukumäärillä. Tämä liittyy siihen, minkäkokoisua lukuja int-tyyppiset muuttujat voivat korkeintaan sisältää. Palaamme tähän myöhemmin kurssilla.

Ohjelmien tekeminen pienissä paloissa

Kun teet ohjelmaa, oli se sitten harjoitustehtävä tai oma projektisi, mieti minkälaisia osia ohjelma tarvitsee toimiakseen, ja etene näitä pieniä osia yksitellen toteuttaen. Jokaisen osan toteuttamisen jälkeen kokeile tähänastisen ohjelmasi toimintaa.

Älä koskaan yritä ratkaista koko ongelmaa kerralla, sillä tällöin ohjelman suorittaminen ja testaaminen kesken ongelmanratkaisuprosessin on vaikeaa. Aloita jollain helpolla asialla jonka tiedät varmasti osaavasi. Kun yksi ohjelman osa on saatu toimimaan, voit siirtyä ratkaisemaan seuraavaa ongelmaa.

Osa kurssin tehtävistä on valmiiksi osiin pilkottuja. Usein osat pitää vielä pilkkoa ohjelmoinnin kannalta vieläkin pienempiin paloihin. Kannattaa tehdä siten, että suoritat ohjelman lähes jokaisen uuden koodirivin jälkeen. Tällöin varmistat, että ratkaisu on etenemässä haluttuun suuntaan.

Seuraavassa tehtävässä tehdään yksi ohjelma, mutta ohjelman rakentaminen tapahtuu hyvin pienissä paloissa. Tämä on ehdottoman suositeltava tapa aina kun ohjelmoit.

Tehtäväsarja muodostaa yhden isomman ohjelman, jonka toiminnallisuus toteutetaan pienissä paloissa. Jos et tee tehtäväsarjaa loppuun asti, voit lähettää sen tarkastettavaksi vajaatekoisenakin. Tämä onnistuu painamalla testausnapin oikealla puolella olevasta "submit"-napista eli pienestä ylöspäinosoittavasta nuolesta. Vaikka palautusautomaatti valittaakin vielä tekemättä olevien tehtävänosien testeistä, kirjautuvat jo tekemiesi osien pisteet.

Huom: nyt (ja jatkossa) jokainen isomman tehtävän "alitehtävä" on saman arvoinen tehtävä kuin alikohdaton tehtävä. Tämä tehtävä vastaa siis viittä normaalia tehtävää.

Lukujen lukeminen

Tee ohjelma, joka kysyy käyttäjältä lukuja (ohjelma tulostaa käyttäjälle aluksi "Syötä luvut:"), kunnes käyttäjä antaa luvun -1. Kun käyttäjä syöttää luvun -1, ohjelma tulostaa "Kiitos ja näkemiin!" ja päättyy.

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!

Lukujen summa

Laajenna edellistä ohjelmaa siten, että ohjelma ilmoittaa käyttäjän syöttämien lukujen summan. (Lukua -1 ei lasketa mukaan.)

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!
Summa: 11

Lukujen summa ja lukumäärä

Laajenna edellistä ohjelmaa siten, että ohjelma ilmoittaa myös käyttäjien antamien lukujen lukumäärän. (Lukua -1 ei lasketa mukaan.)

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!
Summa: 11
Lukuja: 3

Lukujen keskiarvo

Muuta edellistä ohjelmaa siten, ohjelma ilmoittaa lukujen keskiarvon. (Lukua -1 ei lasketa mukaan.)

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!
Summa: 11
Lukuja: 3
Keskiarvo: 3.666666666666

Parilliset ja parittomat

Laajenna edellistä ohjelmaa siten, että ohjelma ilmoittaa parillisten ja parittomien lukujen määrän. (Lukua -1 ei lasketa mukaan.)

Syötä luvut:
5
2
4
-1
Kiitos ja näkemiin!
Summa: 11
Lukuja: 3
Keskiarvo: 3.666666666666
Parillisia: 2
Parittomia: 1

Toistolauseet ja ohjelman tyyli

Tarkastellaan vielä aiemmin näkemämme ohjelman varianttia. Allaolevassa esimerkissä ohjelmoija on toteuttanut lopettamisen jälkeen tapahtuvan toiminnallisuuden osaksi ehtolausetta, missä toistolauseesta poistutaan.

Scanner lukija = new Scanner(System.in);

System.out.print("Anna lukuja, negatiiviset luvut eivät kelpaa: ");
int summa = 0;
int hyvaksytytLuvut = 0;
int epakelvotLuvut = 0;

while (true) {
    int luettu = Integer.parseInt(lukija.nextLine());

    if (luettu == 0) {
        System.out.println("Hyväksyttävien lukujen summa: " + summa);
        System.out.println("Hyväksyttyjä lukuja: " + hyvaksytytLuvut);
        System.out.println("Epäkelvot luvut: " + epakelvotLuvut);
        break;
    }

    if (luettu < 0) {
        epakelvotLuvut++;
        continue;
    }

    summa += luettu;
    hyvaksytytLuvut++;
}

Ylläoleva lähestymistapa ei ole suositeltava, sillä se johtaa helposti hyvin monimutkaiseen ohjelman rakenteeseen. Jos toistolauseen lopettamisen yhteydessä pitäisi tehdä muutakin -- esimerkiksi lukea lisää syötteitä -- asetettaisiin kyseinenkin toiminnallisuus helposti ehtolauseen sisälle, mikä johtaa yhä vaikeammin ymmärrettävään lähdekoodiin.

Pitäydytään seuraavassa toistolauseen muodossa:

Scanner lukija = new Scanner(System.in);

// toistolauseessa tarvittavien muuttujien luominen

while (true) {
    // syötteen lukeminen

    // toistolauseesta poistuminen -- break

    // epäkelpojen syötteiden rajaaminen pois -- continue

    // hyväksyttävien syötteiden käsittely
}

// toistolauseesta poistumisen jälkeen suoritettava toiminnallisuus

Toisin sanoen, yllä oleva ohjelma on selkeämpi jos toistolauseesta poistumisen jälkeen tehtävät asiat ovat toistolauseen ulkopuolella.

Scanner lukija = new Scanner(System.in);

System.out.print("Anna lukuja, negatiiviset luvut eivät kelpaa: ");
int summa = 0;
int hyvaksytytLuvut = 0;
int epakelvotLuvut = 0;

while (true) {
    int luettu = Integer.parseInt(lukija.nextLine());

    if (luettu == 0) {
        break;
    }

    if (luettu < 0) {
        epakelvotLuvut++;
        continue;
    }

    summa += luettu;
    hyvaksytytLuvut++;
}

System.out.println("Hyväksyttävien lukujen summa: " + summa);
System.out.println("Hyväksyttyjä lukuja: " + hyvaksytytLuvut);
System.out.println("Epäkelvot luvut: " + epakelvotLuvut);
Ikuinen debaatti: break

Opittavan ja käytettävän toistolauseen ominaisuuksia on tarkasteltu ohjelmoinnin opetukseen liittyvässä tutkimuksessa pitkään. Eräs debaatti liittyy break-komennon käyttöön. Jotkut ovat sitä mieltä, että sen käyttäminen on yleisesti ottaen hyvä käytäntö, toiset taas ovat sitä mieltä, että se toimii esimerkiksi toistolauseen käyttöön tutustumisessa. Jotkut taas eivät suosi sen käyttämistä lainkaan.

Metodit

Olemme käyttäneet useita erilaisia komentoja Javassa: sijoitusta, laskutoimituksia, vertailuja, if:iä ja whileä. Ruudulle tulostaminen on tehty System.out.println() lauseella. Myös lause Integer.parseInt(lukija.nextLine()) näyttää tutulta.

Huomaamme, että jälkimmäinen joukko edellä lueteltuja komentoja poikkeaa if:istä ja while:stä ym. siinä, että komennon perässä on sulut ja joskus sulkujen sisällä komennolle annettava syöte. "Sulkuihin päättyvät" eivät oikeastaan olekaan komentoja vaan metodeja.

Teknisesti ottaen metodi tarkoittaa nimettyä lauseista koostuvaa joukkoa, jota voi kutsua muualta ohjelmakoodista nimen perusteella. Koodirivi System.out.println("olen metodille annettava parametri!") siis tarkoittaa, että kutsutaan metodia, joka suorittaa ruudulle tulostamisen. Metodin sisäinen toteutus -- eli joukko suoritettavia lauseita -- on tässä tapauksessa java-ohjelmointikielen piilottama.

Metodin suorituksen jälkeen palataan takaisin kohtaan, missä ennen metodikutsua oltiin menossa, ja ohjelman suoritus jatkuu tästä. Metodille suluissa annettua syötettä kutsutaan metodin parametriksi -- metodin parametreilla annetaan metodeille tarkempaa tietoa odotetusta suorituksesta; esimerkiksi tulostuslauseelle kerrotaan parametrin avulla mitä pitäisi tulostaa.

Parametrin lisäksi metodilla voi olla paluuarvo. Esim. tuttu lause:

int luku = Integer.parseInt(lukija.nextLine());

sisältää kaksi metodikutsua. Ensin kutsutaan sisempänä olevaa metodia lukija.nextLine. Metodilla on paluuarvonaan käyttäjän syöttämä merkkijono. Seuraavaksi kutsutaan metodia Integer.parseInt. Metodikutsun parametrina on merkkijono jonka metodin lukija.nextLine kutsu palautti ja metodin paluuarvona on merkkijonoa vastaava kokonaisluku.

Metodin nimeen näyttää liittyvän piste, esim. lukija.nextLine(). Oikeastaan tässä metodin nimi onkin pisteen oikeanpuoleinen osa, eli nextLine(). Pisteen vasemmanpuoleinen osa, eli tässä lukija kertoo kenen metodista on kyse. Eli kyseessä on lukijan metodi nextLine. Opimme hiukan myöhemmin tarkemmin mistä tässä pisteen vasemmanpuoleisessa osassa on kyse. Tarkka lukija tietysti huomaa, että System.out.println():ssa on "kaksi pistettä". Metodin nimi tässä on println, ja System.out on se kenen metodista on kyse. Karkeasti ottaen System.out tarkoittaa koneen näyttöä.

Tähän mennessä käyttämämme metodit ovat kaikki olleet Javan valmiita metodeita. Opetellaan seuraavaksi tekemään omia metodeita.

Omat metodit

Olemme tähän mennessä ohjelmoineet ohjelmamme siten, että kaikki tapahtuu yhdessä jatkumossa ja koodia luetaan ylhäältä alas.

Edellä mainittiin että "metodi tarkoittaa nimettyä lauseista koostuvaa joukkoa, jota voi kutsua muualta ohjelmakoodista nimen perusteella". Javan valmiita metodeja on käytetty oikeastaan ensimmäisestä ohjelmasta lähtien.

Javan valmiiden metodien käytön lisäksi ohjelmoija voi kirjoittaa itse metodeja joita sovellus kutsuu. Oikeastaan on hyvin poikkeuksellista jos ohjelmassa ei ole yhtään itse kirjoitettua metodia. Tästä lähtien lähes jokainen kurssilla tehty ohjelma sisältääkin itsekirjoitettuja metodeja.

Ohjelmarunkoon metodit kirjoitetaan main:in aaltosulkeiden ulkopuolelle mutta kuitenkin "uloimmaisten" aaltosulkeiden sisäpuolelle, joko mainin ylä- tai alapuolelle.

import java.util.Scanner;

public class Esimerkki {
    public static void main(String[] args) {
        Scanner lukija = new Scanner(System.in);
        // ohjelmakoodi
    }

    // omia metodeja tänne
}

Luodaan metodi tervehdi.

public static void tervehdi() {
    System.out.println("Terveiset metodimaailmasta!");
}

Ja asetetaan se metodeille kuuluvalle paikalle.

import java.util.Scanner;

public class Esimerkki {
    public static void main(String[] args) {
        Scanner lukija = new Scanner(System.in);
        // ohjelmakoodi
    }

    // omia metodeja tänne
    public static void tervehdi() {
        System.out.println("Terveiset metodimaailmasta!");
    }
}

Metodin määrittely sisältää kaksi osaa. Metodimäärittelyn ensimmäisellä rivillä on metodin nimi eli tervehdi. Nimen vasemmalla puolella tässä vaiheessa määreet public static void. Metodin nimen sisältävän rivin alla on aaltosulkeilla erotettu koodilohko, jonka sisälle kirjoitetaan metodin koodi, eli ne komennot jotka metodia kutsuttaessa suoritetaan. Metodimme tervehdi ei tee muuta kuin kirjoittaa rivillisen tekstiä ruudulle.

import java.util.Scanner; public class Esimerkki { public static void main(String[] args) { Scanner lukija = new Scanner(System.in); // ohjelmakoodi } // omia metodeja tänne public static void tervehdi() { System.out.println("Terveiset metodimaailmasta!"); } }
import java.util.Scanner; public class Esimerkki { public static void main(String[] args) { Scanner lukija = new Scanner(System.in); // ohjelmakoodi } // omia metodeja tänne // MARK }

Itsekirjoitetun metodin kutsu on helppoa, kirjoitetaan metodin nimi ja perään sulut ja puolipiste. Seuraavassa main eli pääohjelma kutsuu tervehdi-metodia yhteensä neljä kertaa.

import java.util.Scanner;

public class OhjelmaRunko {
    public static void main(String[] args) {
        Scanner lukija = new Scanner(System.in);

        // ohjelmakoodi
        System.out.println("Kokeillaan pääsemmekö metodimaailmaan:");
        tervehdi();

        System.out.println("Näyttää siltä, kokeillaan vielä:");
        tervehdi();
        tervehdi();
        tervehdi();
    }

    // omat metodit
    public static void tervehdi() {
        System.out.println("Terveiset metodimaailmasta!");
    }
}

Ohjelman suoritus saa aikaan seuraavan tulosteen:

Kokeillaan pääsemmekö metodimaailmaan:
Terveiset metodimaailmasta!
Näyttää siltä, kokeillaan vielä:
Terveiset metodimaailmasta!
Terveiset metodimaailmasta!
Terveiset metodimaailmasta!

Huomionarvoista tässä on ohjelman suoritusjärjestys. Ohjelman suoritus etenee siten, että pääohjelman -- eli main:in -- rivit suoritetaan ylhäältä alas yksi kerrallaan. Kun lause on metodikutsu, ohjelman suoritus siirtyy metodiin. Metodin lauseet suoritetaan yksi kerrallaan ylhäältä alas. Tämän jälkeen palataan kohtaan, josta metodin kutsu tapahtui. Tarkemmin ottaen metodikutsun jälkeiselle riville.

Jos ollaan tarkkoja niin pääohjelma eli main on itsekin metodi. Kun ohjelma käynnistyy, kutsuu käyttöjärjestelmä main:ia. Metodi main on siis ohjelman käynnistyspiste, jonka ylimmältä riviltä ohjelman suoritus lähtee liikkeelle. Ohjelman suoritus loppuu kun päädytään mainin loppuun.

Tee metodi tulostaTeksti, joka tulostaa tekstin "Alussa olivat suo, kuokka ja Java." sekä rivinvaihdon.

public static void main(String[] args) {
    tulostaTeksti();
}

public static void tulostaTeksti() {
    // kirjoita koodia tähän
}

Ohjelman tulostus:

Alussa olivat suo, kuokka ja Java.

Laajenna edellistä ohjelmaa siten, että pääohjelma kysyy käyttäjältä, montako kertaa teksti tulostetaan eli montako kertaa metodia kutsutaan.

public static void main(String[] args) {
    // kysy käyttäjältä, montako kertaa teksti tulostetaan
    // kutsu metodia tulostaTeksti while-komennon avulla useita kertoja
}

public static void tulostaTeksti() {
    // kirjoita koodia tähän
}

Ohjelman tulostus:

Kuinka monta?
7
Alussa olivat suo, kuokka ja Java.
Alussa olivat suo, kuokka ja Java.
Alussa olivat suo, kuokka ja Java.
Alussa olivat suo, kuokka ja Java.
Alussa olivat suo, kuokka ja Java.
Alussa olivat suo, kuokka ja Java.
Alussa olivat suo, kuokka ja Java.

Huom: tulosta kehote Kuinka monta? omalle rivilleen!

Jatkossa kun esittelemme metodeja, emme erikseen mainitse että niiden täytyy sijaita omalla paikallaan. Metodia ei esimerkiksi voi määritellä toisen metodin sisällä.

Metodikutsujen suoritus ja kutsupino

Mistä tietokone tietää minne metodin suorituksen jälkeen tulee palata?

Java-lähdekoodin suoritusympäristö pitää kirjaa suoritettavasta metodista kutsupinossa. Kutsupino sisältää kehyksiä, joista jokainen sisältää tiedon kyseisen metodin sisäisistä muuttujista sekä niiden arvoista. Kun metodia kutsutaan, kutsupinoon luodaan uusi kehys, joka sisältää metodin sisältämät muuttujat. Kun metodin suoritus loppuu, metodiin liittyvä kehys poistetaan kutsupinosta, jolloin suoritusta jatketaan kutsupinon edeltävästä metodista.

Alla olevan visualisaation oikealla laidalla näytetään kutsupinon toimintaa. Metodikutsun yhteydessä kutsupinoon luodaan uusi kehys, joka poistetaan metodikutsusta poistuttaessa.

Metodien nimennästä

Metodit nimetään siten, että ensimmäinen sana kirjoitetaan pienellä ja loput alkavat isolla alkukirjaimella, tälläisestä kirjoitustavasta käytetään nimitystä camelCase. Tämän lisäksi, metodin sisällä koodi on sisennetty taas neljä merkkiä.

VäärinOikein
public static void Tama_metodi_sanoo_mur ( ) {
System.out.println("mur");
}
public static void tamaMetodiSanooMur() {
    System.out.println("mur");
}

Metodin parametrit

Parametrit ovat metodille annettavia arvoja, joita käytetään metodin suorituksessa. Metodin parametrit määritellään metodin ylimmällä rivillä metodin nimen jälkeen olevien sulkujen sisällä. Kun metodia kutsutaan, sen parametreille annetaan arvot kutsuvaiheessa.

Seuraavassa esimerkissä määritellään parametrillinen metodi tervehdi, jolla on int-tyyppinen parametri montakoKertaa.

public static void tervehdi(int montakoKertaa) {
    int i = 0;
    while (i < montakoKertaa) {
        System.out.println("Tervehdys!");
        i++;
    }
}

Kutsutaan metodia tervehdi siten, että parametrin montakoKertaa arvoksi asetetaan ensimmäisellä kutsulla 1 ja toisella kutsulla 3.

public static void main(String[] args) {
    tervehdi(1);
    tervehdi(3);
}
Tervehdys!
Tervehdys!
Tervehdys!
Tervehdys!

Aivan kuten Javan valmista System.out.println()-metodia kutsuttaessa, voi oman metodin kutsussa parametrina antaa lausekkeen.

public static void main(String[] args) {
    tervehdi(1 + 2);
}
Tervehdys!
Tervehdys!
Tervehdys!

Jos metodia kutsuttaessa parametriksi määritellään lauseke, evaluoidaan lauseke ennen metodikutsua. Yllä metodikutsun parametri evaluoituu arvoksi 3 ja lopullinen metodikutsu on muotoa tervehdi(3);.

Monta parametria

Metodille voidaan määritellä useita parametreja. Tällöin metodin kutsussa parametrit annetaan samassa järjestyksessä.

public static void summa(int eka, int toka) {
    System.out.println("Lukujen " + eka + " ja " + toka + " summa on " + (eka + toka));
}
summa(3, 5);

int luku1 = 2;
int luku2 = 4;

summa(luku1, luku2);
Lukujen 3 ja 5 summa on 8
Lukujen 2 ja 4 summa on 6
Metodien merkintä vuokaavioon

Metodikutsut merkitään reunallisilla neliöillä vuokaavioihin ja metodien toiminta piirretään tyypillisesti omina erillisinä vuokaavioinaan. Käytännössä alla oleva ohjelma piirrettäisiin siis kahdessa osassa.

public class Esimerkki {
    public static void main(String[] args) {
        summa(3, 5);

        int luku1 = 2;
        int luku2 = 4;

        summa(luku1, luku2);
    }

    public static void summa(int eka, int toka) {
        System.out.println("" + eka + " + " + toka + " = " + (eka + toka));
    }
}

Pääohjelma (main-metodi):

Tehty flowchart.js.org-osoitteessa. Koodi: st=>start: Alku

					   sr=>subroutine: summa(3, 5)

					   op=>operation: luku1 = 2
					   op2=>operation: luku2 = 4


					   sr2=>subroutine: summa(luku1, luku2)

					   e=>end: Loppu

					   st->sr->op->op2->sr2->e

Summa-metodi:

Tehty flowchart.js.org-osoitteessa. Koodi: st=>start: summa (luku1, luku2)

					    io=>inputoutput: eka + " + " + toka + " = " + (eka + toka)

					    e=>end: Loppu

					    st->io->e

Yllä olevassa vuokaaviossa summa-metodin vuokaavion alkupiste on nimetty metodin nimen mukaan. Tämä ei ole kuitenkaan pakollista -- oleellista vuokaavioilla on päätyä tilanteeseen, missä ne helpottavat ohjelman suorituksen ymmärtämistä.

Kirjoita metodi public static void jakolasku(int osoittaja, int nimittaja), joka tulostaa osoittajan ja nimittäjän jakolaskun tuloksen (osoittaja / nimittaja). Muistuta mieleen mitä omituisuuksia jakolaskuihin liittyi.

TMC:n mukana tulevat testit eivät testaa metodin toiminnan oikeellisuutta. Tässä -- kuten yleensäkin -- on hyvä testata metodin toimintaa itse. Ennen kuin testaat metodia, mieti minkälaisilla syötteillä pitäisi tulla minkälaisia tulostuksia ja kokeile ainakin kolmea erilaista vaihtoehtoa. Testaa myös, että "ei tasan" menevät jakolaskut toimivat oikein.

Parametrien arvot kopioituvat

Metodikutsun yhteydessä parametrien arvot kopioituvat. Tämä tarkoittaa käytännössä sitä, että sekä main-metodissa että kutsuttavassa metodissa voi olla saman nimiset muuttujat, mutta muuttujien arvon muuttaminen kutsuttavan metodin sisällä ei muuta main-metodissa olevan muuttujan arvoa. Tarkastellaan tätä seuraavan ohjelman avulla.

public class Esimerkki {
    public static void main(String[] args) {
        int mista = 5;
        int mihin = 10;

        tulostaLuvut(mista, mihin);

        mista = 8;

        tulostaLuvut(mista, mihin);
    }

    public static void tulostaLuvut(int mista, int mihin) {
        while (mista < mihin) {
            System.out.println(mista);
            mista++;
        }
    }
}

Ohjelman tulostus on seuraava:

5
6
7
8
9
8
9

Alla sama askeleittaisena visualisaationa. Huomaat että main-metodissa olevat arvot jäävät kutsupinoon odottamaan metodin tulostaLuvut suorittamista. Metodissa tulostaLuvut olevien muuttujien arvojen muuttaminen ei muuta metodin main muuttujien arvoja, vaikka ne ovatkin saman nimisiä.

Metodi kutsuu toista metodia

Metodin sisältä voi kutsua myös muita metodeja. Tehdään metodi kertotaulu, joka tulostaa annetun luvun kertotaulun. Kertotaulu tulostaa rivit metodin tulostaKertotaulunRivi avulla.

public static void kertotaulu(int ylaraja) {
    int luku = 1;

    while (luku <= ylaraja) {
        tulostaKertotaulunRivi(luku, ylaraja);
        luku++;
    }
}

public static void tulostaKertotaulunRivi(int luku, int kerroin) {

    int tulostettava = luku;
    while (tulostettava <= luku * kerroin) {
        System.out.print("  " + tulostettava);
        tulostettava += luku;
    }

    System.out.println("");
}

Esimerkiksi metodikutsun kertotaulu(3) tulostus on seuraava.

1  2  3
2  4  6
3  6  9

Alla metodikutsu kertotaulu(3) visualisoituna. Huomaa, miten kutsupinossa on tieto kutsuvan metodin sisäisestä tilasta.

Tähtien tulostus

Tee metodi tulostaTahtia, joka tulostaa annetun määrän tähtiä ja rivinvaihdon.

Tee metodi seuraavaan runkoon:

public static void tulostaTahtia(int maara) {
    // yhden tähden saat tulostettua komennolla
    // System.out.print("*");
    // kutsu tulostuskomentoa n kertaa
    // tulosta lopuksi rivinvaihto komennolla
    // System.out.println("");
}

public static void main(String[] args) {
    tulostaTahtia(5);
    tulostaTahtia(3);
    tulostaTahtia(9);
}

Ohjelman tulostus:

*****
***
*********

huom: moniosaisen tehtävät voi palauttaa palvelimelle (painamalla testausnapin oikealla puolella olevaa nappia) vaikka kaikki osat eivät olisikaan tehty. Palvelin valittelee tällöin tekemättömien osien testeistä, tehdyt osat palvelin kirjaa.

Neliön tulostus

Tee metodi tulostaNelio(int sivunpituus) joka tulostaa neliön käyttäen tulostaTahtia-metodia. Siis esimerkiksi kutsu tulostaNelio(4) tulostaa seuraavaa:

****
****
****
****

Huom: tehtävässä ei riitä että tulostus näyttää oikealta, tulostaNelio-metodin sisällä neliön "rivien" tulostus tulee tehdä tulostaTahtia-metodia käyttäen.

Ohjelmaa tehdessäsi kannattaa varmistaa main:iin kirjoitetun testikoodin avulla että metodit toimivat vaaditulla tavalla.

Suorakulmion tulostus

Tee metodi tulostaSuorakulmio(int leveys, int korkeus) joka tulostaa suorakulmion käyttäen tulostaTahtia-metodia. Siis esimerkiksi kutsu tulostaSuorakulmio(17,3) tulostaa seuraavaa:

*****************
*****************
*****************

Vasemmalle nojaavan kolmion tulostus

Tee metodi tulostaKolmio(int koko) joka tulostaa kolmion käyttäen tulostaTahtia-metodia. Siis esimerkiksi kutsu tulostaKolmio(4) tulostaa seuraavaa:

*
**
***
****

Tähtirivin ja tyhjien tulostus

Tee metodi tulostaTyhjaa(int maara) joka tulostaa maara kappaletta välilyöntejä. Metodi ei tulosta rivinvaihtoa.

Joudut myös joko kopioimaan edellisen tehtävän vastauksestasi metodin tulostaTahtia tai toteuttamaan sen uudelleen tämän tehtävän tehtäväpohjaan.

Oikealle nojaavan kolmion tulostus

Tee metodi tulostaKolmio(int koko) joka tulostaa kolmion käyttäen tulostaTyhjaa- ja tulostaTahtia-metodeja. Siis esimerkiksi kutsu tulostaKolmio(4) tulostaa seuraavaa:

   *
  **
 ***
****

Joulukuusen tulostus

Tee metodi jouluKuusi(int korkeus) joka tulostaa joulukuusen. Joulukuusi koostuu annetun korkuisesta kolmiosta ja jalasta. Jalka on kaksi tähteä korkea ja kolme tähteä leveä ja se on keskellä kolmion pohjaa. Kuusi tulee rakentaa käyttämällä tulostukseen metodeja tulostaTyhjaa ja tulostaTahtia

Esimerkiksi kutsu jouluKuusi(4) tulostaa seuraavaa:

   *
  ***
 *****
*******
  ***
  ***

Kutsu jouluKuusi(10) tulostaa:

         *
        ***
       *****
      *******
     *********
    ***********
   *************
  ***************
 *****************
*******************
        ***
        ***

Huom: Korkeuksien jotka ovat alle 3 ei tarvitse toimia!

Tässä tehtävässä luodaan seuraavanlainen numerovisa:

Arvaa luku: 73
Luku on pienempi, tehtyjä arvauksia: 1
Arvaa luku: 22
Luku on suurempi, tehtyjä arvauksia: 2
Arvaa luku: 51
Luku on suurempi, tehtyjä arvauksia: 3
Arvaa luku: 62
Luku on suurempi, tehtyjä arvauksia: 4
Arvaa luku: 68
Luku on suurempi, tehtyjä arvauksia: 5
Arvaa luku: 71
Luku on pienempi, tehtyjä arvauksia: 6
Arvaa luku: 70
Onneksi olkoon, oikein arvattu!

Numeron arvaaminen

Tehtävänannon mukana tulevassa ohjelmassa tulee mukana komento arvoLuku, joka arpoo luvun suljetulta väliltä [0, 100] (0 ja 100 ovat myös mahdollisia). Toteuta ohjelma jossa arvataan arvottua lukua kerran. Ohjelman tulee tulostaa joko "Luku on pienempi", "Luku on suurempi" tai "Onneksi olkoon, oikein arvattu!" riippuen käyttäjän antamasta luvusta.

Arvaa luku: 12
Luku on suurempi
Arvaa luku: 66
Luku on pienempi
Arvaa luku: 42
Onneksi olkoon, oikein arvattu!

Toistuva arvaaminen

Lisää ohjelmaan toiminnallisuus jossa arvausta tehdään toistuvasti kunnes käyttäjä syöttää oikean numeron. Huomaa, että sinun tulee arpoa numero komentoa arvoLuku ennen toistolauseketta. Miksi? Mitä tapahtuu, jos luku arvotaan toistolausekkeen sisällä?

Alla olevassa esimerkissä kutsu komentoon arvoLuku palautti arvon 83.

Arvaa luku: 55
Luku on suurempi
Arvaa luku: 85
Luku on pienempi
Arvaa luku: 77
Luku on suurempi
Arvaa luku: 81
Luku on suurempi
Arvaa luku: 83
Onneksi olkoon, oikein arvattu!

Arvauskertojen laskeminen

Lisää ohjelmaan kokonaislukutyyppinen muuttuja, jonka avulla pidetään kirjaa tehtyjen arvausten määrästä. Tulosta arvausten määrä aina arvauksen yhteydessä.

Arvaa luku: 55
Luku on suurempi, tehtyjä arvauksia: 1
Arvaa luku: 85
Luku on pienempi, tehtyjä arvauksia: 2
Arvaa luku: 77
Luku on suurempi, tehtyjä arvauksia: 3
Arvaa luku: 81
Luku on suurempi, tehtyjä arvauksia: 4
Arvaa luku: 83
Onneksi olkoon, oikein arvattu!

Yhteenveto

Materiaalin toisessa osassa keskityttiin toistolauseen ja muuttujien yhteistoimintaan sekä metodien toimintaan. Arvojen askeleittainen läpikäynti toistolauseen ja muuttujien kanssa esiintyy oikeastaan lähes jokaisessa arkisessa sovelluksessa. Esimerkiksi uutissivustolla näkyvät uutiset tulostetaan näkyville yksi kerrallaan toistolausetta käyttäen, kännykän yhteystiedot tulostetaan näkyville yksi kerrallaan toistolausetta käyttäen ja niin edelleen. Metodeja taas hyödynnetään toistuvien ohjelmakoodien eriyttämiseen sekä ohjelmakoodin ylläpidettävyyden ja luettavuuden parantamiseen. Tämä kaikki tulee kurssilla hiljalleen tutummaksi ja tutummaksi.

Toisen osan yhteenvedossa tarkastelet toistolauseiden ja muuttujien käyttämistä hieman toisenlaisessa kontekstissa. Lähtökohtana ohjelmallemme on graafinen käyttöliittymä, jonka lähdekoodi on kuvattuna alla.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

// Luokka (ohjelma) Puisevaa "perii" graafisen käyttöliittymän rakentamiseen
// tarvittavia ominaisuuksia Application-luokalta (ohjelmalta)
public class Puisevaa extends Application {

    // Käyttöliittymän käynnistäminen kutsuu metodia alla olevaa
    // metodia
    @Override
    public void start(Stage ikkuna) throws Exception {

        // luodaan 640 pikseliä leveä ja 480 pikseliä korkea alusta
        // piirtämiselle ja otetaan alustaan liittyvä piirtoväline
        // käyttöön
        Canvas piirtoalusta = new Canvas(640, 480);
        GraphicsContext piirturi = piirtoalusta.getGraphicsContext2D();

        // kutsutaan piirtämiseen käytettävää metodia piirrä
        // -- metodiin tullaan toteuttamaan piirtotoiminnallisuus
        piirra(piirturi);

        // asetetaan käyttöliittymän osat paikalleen ja
        // näytetään se käyttäjälle
        Pane ruutu = new Pane(piirtoalusta);
        Scene nakyma = new Scene(ruutu);

        ikkuna.setTitle("Niin hyvää puuta!");
        ikkuna.setScene(nakyma);
        ikkuna.show();
    }

    // Ohjelma käynnistetään metodilla public static void main.
    // Metodissa kutsutaan Application luokalta perittyä toiminnallisuutta,
    // joka lopulta suorittaa yllä kuvatun start-metodin
    public static void main(String[] args) {
        launch(Puisevaa.class);
    }

    // Oma piirra-metodimme, jonne toteutetaan piirtotoiminnallisuus
    public static void piirra(GraphicsContext piirturi) {

        // viivan piirtäminen kohdasta (0,0) kohtaan (100, 100)
        piirturi.strokeLine(0, 0, 100, 100);

    }
}

Yllä kuvatun ohjelman käynnistäminen näyttää alla olevassa kuvassa näkyvän ikkunan. Ikkunan otsikko on "Niin hyvää puuta!" ja ikkunassa näkyy piirretty viiva. Tietokoneohjelmissa piirtäminen tapahtuu siten, että origo eli piste (0, 0) on ikkunan vasemmassa yläkulmassa, ja y-koordinaatti kasvaa alaspäin mennessä.

Ikkunan otsikko on 'Niin hyvää puuta!' ja ikkunassa näkyy piirretty viiva.

 

Metodin public static void piirra(GraphicsContext piirturi) parametrina saama piirturi on kytketty ohjelman ikkunaan. Kaikki piirturin piirtokomennot siis piirtävät ikkunaan.

Oleellisia komennot tällä hetkellä ovat seuraavat:

  • piirturi.strokeLine(int mistaX, int mistaY, int mihinX, int mihinY) piirtää viivan kohdasta (mistaX, mistaY) kohtaan (mihinX, mihinY).
  • piirturi.strokeRect(int mistaX, int mistaY, int leveys, int korkeus) piirtää suorakulmion, jonka leveys on leveys ja korkeus on korkeus. Suorakulmion vasen yläkulma on pisteessä (mistaX, mistaY).
  • piirturi.strokeOval(int mistaX, int mistaY, int leveys, int korkeus) piirtää ovaalin, jonka leveys on leveys ja korkeus on korkeus. Pienimmän mahdollisimman suorakulmion, joka rajaa ovaalin, vasen yläkulma on pisteessä (mistaX, mistaY)

Alla olevassa esimerkissä on käytetty jokaista komentoa.

// Oma piirra-metodimme, jonne toteutetaan piirtotoiminnallisuus
public static void piirra(GraphicsContext piirturi) {

    piirturi.strokeLine(0, 0, 300, 300);
    piirturi.strokeRect(0, 0, 300, 300);
    piirturi.strokeOval(0, 0, 300, 300);

}
Ikkunaan on piirretty viiva, suorakulmio sekä ovaali.

 

Voimme hyödyntää piirtämisessä myös toistolauseita. Alla on hahmoa toistetaan 10 kertaa.

// Oma piirra-metodimme, jonne toteutetaan piirtotoiminnallisuus
public static void piirra(GraphicsContext piirturi) {

    int kerta = 0;
    int leveys = 40;
    int korkeus = 30;

    while (kerta < 10) {

        piirturi.strokeLine(kerta * leveys, kerta * korkeus, (kerta + 1) * leveys, (kerta + 1) * korkeus);
        piirturi.strokeRect(kerta * leveys, kerta * korkeus, leveys, korkeus);
        piirturi.strokeOval(kerta * leveys, kerta * korkeus, leveys, korkeus);

        kerta++;
    }

}
Toistuva hahmo.

 

Tässä tehtävässä tavoitteenasi on luoda ohjelma, joka piirtää käyttäjälle näytettävään ikkunaan alla kuvatun puujonon.

Viisi puuta rinnakkain

 

Toteuta toiminnallisuus tehtäväpohjassa olevaan metodiin piirra -- voit myös kokeilla omien metodiesi luomista.

Tehtävään ei ole automaattisia testejä. Kun olet saanut puujonon luotua, palauta tehtävä.

Sisällysluettelo