Osa 2

Ongelman osia ja niiden ratkaisumalleja

:

:

Kirjaudu sisään nähdäksesi tehtävän.

Samat pienet ongelmat (tai "osaongelmat") toistuvat ohjelmissa yhä uudestaan ja uudestaan: "Lue käyttäjältä syötettä", "Laske lukujen summa", "Laske lukujen keskiarvo", "Lue käyttäjältä syötettä kunnes", "Montako lukua käyttäjä on syöttänyt.", jne.

Tarkastellaan muutamia tällaisia ongelmia sekä niihin liittyviä ratkaisuja.

Lukemista

Ratkaisumalli käyttäjältä lukemista vaativiin ohjelmointiongelmiin on suoraviivainen. Mikäli ohjelmassa halutaan lukea käyttäjältä syötettä, tulee ohjelmaan luoda syötteen lukemiseen käytettävä Scanner-apuväline. Scanner-apuväline luodaan pääohjelman (eli lauseen public static void main(String[] args) {) jälkeiselle riville. Scanner-apuvälineen käyttö vaatii, että ohjelman määrittelyä (public class) edeltävillä riveillä on lause import java.util.Scanner;, joka tuo Scanner-apuvälineen ohjelman tietoon.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {
    public static void main(String[] main) {
        // Luodaan lukemiseen käytettävä Scanner-apuväline
        Scanner lukija = new Scanner(System.in);

        // Esimerkkinä eri tyyppisten muuttujien lukemista
        String merkkijono = lukija.nextLine();
        int luku = Integer.valueOf(lukija.nextLine());
        double liukuluku = Double.valueOf(lukija.nextLine());
        boolean totuusarvo = Boolean.valueOf(lukija.nextLine());

    }
}

Laskemista

Ohjelmissa tulee usein laskea asioita kuten lukujen keskiarvoa tai lukujen summaa. Ratkaisumalli tällaisissa ohjelmissa on seuraava.

  1. Määrittele laskemiseen tarvittavat syötteet ja luo niitä varten muuttujat. Ohjelman syötteitä ovat laskemisessa käytettävät arvot. Syötteiden tyypit tunnistaa tyypillisesti ongelma-alueen kuvauksesta.
  2. Selvitä tehtävä laskuoperaatio ja luo laskuoperaation tulokselle muuttuja. Tee ohjelman syötteiden perusteella lasku, jonka arvo asetetaan laskuoperaation tulokselle varattuun muuttujaan. Myös laskuoperaation tuloksen tyypin tunnistaa ongelma-alueen kuvauksesta.
  3. Kun lasku on laskettu, tee jotain laskun tuloksella. Tämä voi olla esimerkiksi laskuoperaation tuloksen tulostaminen, tai vaikkapa keskiarvon laskemisen yhteydessä lukujen summan jakamista lukujen määrällä.

Esimerkiksi ongelman Tee ohjelma, jonka avulla voidaan laskea kahden kokonaisluvun summa. ratkaisumalli on seuraava.

// Määritellään syötteet ja luodaan niitä varten muuttujat
int eka = 1;
int toka = 2;

// Selvitetään laskuoperaatio ja luodaan laskuoperaation
// tulokselle muuttuja
int summa = eka + toka;

// Tulostetaan laskuoperaation tulos
System.out.println("Lukujen " + eka + " ja " + toka + " summa on " + summa);

Sekä lukemista että laskemista sisältävä ohjelma yhdistää edelliset ratkaisumallit. Kahden käyttäjältä pyydetyn luvun tulon laskeva ohjelma on seuraavanlainen.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {
    public static void main(String[] main) {
        // Luodaan lukemiseen käytettävä Scanner-apuväline
        Scanner lukija = new Scanner(System.in);

        // Määritellään syötteet ja luodaan niitä varten muuttujat
        int eka = 1;
        int toka = 2;

        // Luetaan luvut käyttäjältä
        eka = Integer.valueOf(lukija.nextLine());
        toka = Integer.valueOf(lukija.nextLine());

        // Selvitetään laskuoperaatio ja luodaan laskuoperaation
        // tulokselle muuttuja
        int tulo = eka * toka;

        // Tulostetaan laskuoperaation tulos
        System.out.println("Lukujen " + eka + " ja " + toka + " tulo on " + tulo);

    }
}

Edellä olevassa esimerkissä ohjelma on toteutettu niin, että muuttujat määritellään ensin ja niihin luetaan arvot vasta tämän jälkeen. Muuttujien määrittelyn sekä niiden arvojen lukemisen voi myös yhdistää.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {
    public static void main(String[] main) {
        // Luodaan lukemiseen käytettävä Scanner-apuväline
        Scanner lukija = new Scanner(System.in);

        // Määritellään syötteet ja luetaan niihin arvot
        int eka = Integer.valueOf(lukija.nextLine());
        int toka = Integer.valueOf(lukija.nextLine());

        // Selvitetään laskuoperaatio ja luodaan laskuoperaation tulokselle muuttuja
        int tulo = eka * toka;

        // Tulostetaan laskuoperaation tulos
        System.out.println("Lukujen " + eka + " ja " + toka + " tulo on " + tulo);

    }
}
Loading
Loading

Jos ... niin ...

Ongelmat sisältävät usein vaihtoehtoista toiminnallisuutta. Tällaisen toteuttamiseen käytetään ehtolauseita. Ehtolause alkaa if-komennosta, jota seuraa suluissa oleva lauseke. Lauseke evaluoituu joko todeksi tai epätodeksi. Mikäli lauseke evaluoituu todeksi, suoritetaan ehtolauseen lohko, joka on rajattuna aaltosuluilla.

// jos luku on suurempi kuin viisi
if (luku > 5) {
    // niin...
}

Ohjelma, joka tulostaa "ok" kun ohjelmassa olevan lukumuuttujan arvo on suurempi kuin 42, mulloin "ei ok", toteutetaan seuraavasti.

int luku = 15;
if (luku > 42) {
    System.out.println("ok");
} else {
    System.out.println("ei ok")
}

Ehtolauseiden ketjuttaminen on mahdollista. Tällöin ongelmat ovat muotoa "jos a, niin b; muulloin jos c, niin d; muulloin jos e, niin f; muulloin g". Ketjutus toteutetaan if-komennon lohkoa seuraavasta else if -komennosta, johon liittyy oma lauseke sekä lohko. Lopuksi tulee elsekomento sekä siihen liittyvä lohko.

// jos luku on suurempi kuin viisi
if (luku > 5) {
    // toiminnallisuus kun luku suurempi kuin viisi
} else if (luku < 0) { // muulloin jos luku on pienempi kuin nolla
    // toiminnallisuus kun luku on pienempi kuin nolla
    // ja luku EI OLE suurempi kuin viisi
} else {  // muulloin
    // toiminnallisuus muulloin
}
:

:

Kirjaudu sisään nähdäksesi tehtävän.

Ehtotoiminnallisuutta voi yhdistää myös muiden ratkaisumallien kanssa. Tarkastellaan ongelmaa "Lue käyttäjältä kaksi lukua. Mikäli lukujen summa on yli 100, tulosta käyttäjälle merkkijono liikaa. Mikäli lukujen summa on alle 0, tulosta käyttäjälle merkkijono liian vähän. Muulloin, tulosta käyttäjälle merkkijono ok.". Ohjelma, joka yhdistää lukemisen, laskemisen ja ehtolauseen on annettu alla.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {
    public static void main(String[] main) {
        // Luodaan lukemiseen käytettävä Scanner-apuväline
        Scanner lukija = new Scanner(System.in);

        // Määritellään syötteet ja luetaan niihin arvot
        int eka = Integer.valueOf(lukija.nextLine());
        int toka = Integer.valueOf(lukija.nextLine());

        // Selvitetään laskuoperaatio ja luodaan laskuoperaation
        // tulokselle muuttuja
        int summa = eka + toka;

        // Tehdään laskuoperaation tuloksella jotain. Tässä
        // toteutetaan ongelman vaativaa ehdollista toimintaa

        if (summa > 100) { // jos summa on yli sata
            System.out.println("liikaa");
        } else if (summa < 0) { // jos summa on yli alle 0
            System.out.println("liian vähän");
        } else { // muulloin
            System.out.println("ok");
        }
    }
}
Loading
Loading

Lue käyttäjältä kunnes

Ongelmat, jotka pyytävät lukemaan käyttäjältä syötettä kunnes käyttäjä syöttää tietynlaisen syötteen, ratkeavat toistolauseen avulla. Kohtaa "kunnes" vastaa toistolauseissa ehtolause, joka sisältää toistolauseesta poistumiseen johtavan break-komennon.

Alla oleva malli sisältää "lue käyttäjältä kunnes"-tyyppisten ohjelmien peruspalat. Nämä ovat ikuisesti toistaminen eli while (true), toistolauseen sisällä oleva lukeminen eli tässä String luettu = lukija.nextLine();, ja toistolauseesta poistuminen eli toistolauseen sisällä olevan if-komennon sisältämä break-komento. Alla toistolauseesta poistutaan kun käyttäjä syöttää merkkijonon "lopeta".

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {

    public static void main(String[] args) {
        // Luodaan lukemiseen käytettävä Scanner-apuväline
        Scanner lukija = new Scanner(System.in);

        // luetaan käyttäjältä kunnes käyttäjä syöttää
        // merkkijonon lopeta
        while (true) {
            String luettu = lukija.nextLine();

            if (luettu.equals("lopeta")) {
                break;
            }

            // lukemisen jälkeen tehtävä toiminnallisuus

        }

        // toistolauseen jälkeen tehtävä toiminnallisuus

    }
}
Loading

Kuten muissa ratkaisumalleissa, myös muotoa "lue käyttäjältä kunnes" olevien ongelmien ratkaisumallit voi yhdistää muiden ongelmien ratkaisumallien kanssa. Tarkastellaan seuraavaksi hieman laajempaa esimerkkiä, mikä demonstroi tämän.

Lue käyttäjältä kokonaislukuja kunnes käyttäjä syöttää luvun nolla. Tämän jälkeen ohjelman tulee tulostaa merkkijono "optimistista" mikäli käyttäjän syöttämien lukujen keskiarvo on suurempi kuin nolla. Mikäli käyttäjän syöttämien lukujen keskiarvo on pienempi kuin nolla, tulee ohjelman tulostaa merkkijono "pessimististä". Muulloin ohjelman tulee tulostaa merkkijono "neutraalia meininkiä". Lukua nolla ei tule huomioida keskiarvoa laskettaessa.

Yllä kuvatussa tehtävänannossa on monta osaa:

  • lukeminen: ohjelman tulee lukea käyttäjältä kokonaislukuja
  • laskeminen: ohjelman tulee laskea käyttäjän syöttämien lukujen keskiarvo
  • ehdollinen toiminta (jos ... niin ...): ohjelman lopussa tehtävä tulostus riippuu käyttäjän syöttämien lukujen keskiarvosta
  • toistolause (lue käyttäjältä kunnes): ohjelman tulee lukea käyttäjältä syötettä kunnes käyttäjä syöttää luvun nolla

Tarkastellaan näitä osia ensin erikseen.

Kokonaislukujen lukeminen: Kokonaislukujen lukemista varten tarvitaan Scanner-apuväline. Kokonaisluvun lukeminen tapahtuu lukemalla käyttäjältä merkkijono, joka muunnetaan kokonaisluvuksi komennolla Integer.valueOf.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {

    public static void main(String[] args) {
        // Luodaan lukemiseen käytettävä Scanner-apuväline
        Scanner lukija = new Scanner(System.in);

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

    }
}
Keskiarvon laskeminen: lukujen keskiarvon laskemiseen tarvitaan lukujen lukumäärä sekä lukujen summa. Kun nämä ovat tiedossa, saadaan keskiarvo laskettua jakamalla summa lukumäärällä. Huomaa, että nollalla jakaminen ei ole matemaattisessa mielessä sallittua, kts. https://fi.wikipedia.org/wiki/Nollalla_jakaminen. Joissakin ohjelmointikielissä tämä on kuitenkin mahdollista. Mikäli ohjelmassa jakaa luvun nollalla, on lopputulos erittäin harvoin toivottu.
int lukuja = 42;
int summa = 1;

double keskiarvo = 1.0 * summa / lukuja;

Ehdollinen toiminta: ehdollista toimintaa tehdään ehtolauseen avulla. Alla on kuvattuna ohjelmaan kuuluva keskiarvoon liittyvä ehdollinen toiminta.

double keskiarvo = 0.0;
if (keskiarvo < 0) {
    System.out.println("pessimististä");
} else if (keskiarvo > 0) {
    System.out.println("optimistista");
} else {
    System.out.println("neutraalia")
}

Lukeminen kunnes: lukeminen "kunnes" tapahtuu toistolauseen avulla. Toistolause sisältää ehtolauseen, jossa poistutaan toistolauseesta kun käyttäjä syöttää halutun luvun.

Scanner lukija = new Scanner(System.in);

while (true) {
    String syote = lukija.nextLine();

    if (syote.equals("lopeta")) {
        break;
    }
}

Ratkaisumallien toiminta on erillisinä paloina selkeää. Aloitetaan ratkaisumallien yhdistäminen. Yhdistetään ensin kokonaisluvun lukeminen ja lukeminen kunnes. Kokonaisluvun lukeminen tapahtuu osana ehtolausetta.

Scanner lukija = new Scanner(System.in);

while (true) {
    int luku = Integer.valueOf(lukija.nextLine());

    if (luku == 0) {
        break;
    }
}

Yhdistetään edelliseen kokonaisuuteen eli kokonaisluvun lukemiseen kunnes käyttäjä syöttää luvun nolla keskiarvon laskeminen. Keskiarvon laskemiseen — tai laskemiseen liittyvään ratkaisumalliin yleensä — kuuluu muuttujien määrittely, laskenta, sekä jotain laskun tuloksella tekeminen. Koska keskiarvo lasketaan käyttäjän syöttämistä luvuista ja lukuja lasketaan kunnes käyttäjä syöttää tietyn luvun, tulee lukujen lukumäärän ja summan laskeminen lisätä osaksi toistolausetta.

Scanner lukija = new Scanner(System.in);

// lukujen määrittely
int lukuja = 0;
int summa = 0;

while (true) {
    int luku = Integer.valueOf(lukija.nextLine());

    if (luku == 0) {
        break;
    }

    // summan ja lukujen lukumäärän laskeminen
    lukuja = lukuja + 1;
    summa = summa + luku;
}

// keskiarvon laskeminen
double keskiarvo = 1.0 * summa / lukuja;

Yhdistetään lopulta ohjelmaan vaihtoehtoinen toiminnallisuus, joka tekee asioita käyttäjän syötteen perusteella.

Scanner lukija = new Scanner(System.in);

// lukujen määrittely
int lukuja = 0;
int summa = 0;

while (true) {
    int luku = Integer.valueOf(lukija.nextLine());

    if (luku == 0) {
        break;
    }

    // summan ja lukujen lukumäärän laskeminen
    lukuja = lukuja + 1;
    summa = summa + luku;
}

// keskiarvon laskeminen
double keskiarvo = 1.0 * summa / lukuja;

// vaihtoehtoinen toiminta keskiarvon perusteella
if (keskiarvo < 0) {
    System.out.println("pessimististä");
} else if (keskiarvo > 0) {
    System.out.println("optimistista");
} else {
    System.out.println("neutraalia")
}

On hyvä kysymys, mikäli alkuperäisen ongelman antaja tietää ettei nollalla lukeminen ole sallittua, tai että nollalla jakaminen johtaa mahdollisesti virheeseen. Mikäli voimme olettaa, että ongelman antaja tietää tilanteesta (esim. hän kertoo siitä), ei poikkeustilanteeseen tarvitse varautua. Mikäli taas on mahdollista, että ongelman antaja ei ole ajatellun nollalla jakamista, ohjelmaan on hyvä toteuttaa erillinen toiminnallisuus, joka estää nollalla jakamisen.

Eräs mahdollinen kokonaisvaltainen ratkaisu alkuperäiseen ongelmaan on seuraava.

Scanner lukija = new Scanner(System.in);

// lukujen määrittely
int lukuja = 0;
int summa = 0;

while (true) {
    int luku = Integer.valueOf(lukija.nextLine());

    if (luku == 0) {
        break;
    }

    // summan ja lukujen lukumäärän laskeminen
    lukuja = lukuja + 1;
    summa = summa + luku;
}

if (lukuja == 0) {
    System.out.println("Ei syötteitä.");
} else {
    // keskiarvon laskeminen
    double keskiarvo = 1.0 * summa / lukuja;

    // vaihtoehtoinen toiminta keskiarvon perusteella
    if (keskiarvo < 0) {
        System.out.println("pessimististä");
    } else if (keskiarvo > 0) {
        System.out.println("optimistista");
    } else {
        System.out.println("neutraalia")
    }
}
Loading
Loading
Loading
Loading

Lue käyttäjältä kunnes ja rajaa

Ongelmat, jotka pyytävät lukemaan käyttäjältä syötettä kunnes käyttäjä syöttää tietynlaisen syötteen, sekä rajaamaan syötteestä käyttöön vain tietynlaiset arvot, ratkeavat myös toistolauseen avulla. Kohtaa "kunnes" vastaa toistolauseissa break-komennon ja rajaamiseen käytetään toistolauseen alkuun siirtymistä kuvaavaa komentoa continue.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {

    public static void main(String[] args) {
        // Luodaan lukemiseen käytettävä Scanner-apuväline
        Scanner lukija = new Scanner(System.in);

        // lue käyttäjältä kunnes -- toistolause
        while (true) {
            String syote = lukija.nextLine();

            // kunnes
            if (syote.equals("lopeta")) { // poistuminen
                break;
            }

            // rajausehto
            int luku = Integer.valueOf(syote);
            if (luku > 10) { // rajaus
                continue;
            }

            // lukemisen jälkeen tehtävä toiminnallisuus
        }

        // toistolauseen jälkeen tehtävä toiminnallisuus

    }
}

Alla on kuvattuna ohjelma, missä syötteitä luetaan kunnes, jonka lisäksi myös syötteitä rajataan.

Kirjoita ohjelma, joka lukee käyttäjältä kokonaislukuja kunnes käyttäjä syöttää luvun nolla. Tämän jälkeen ohjelma tulostaa niiden lukujen lukumäärän, jotka ovat pienempiä kuin kymmenen mutta suurempia kuin nolla.

Syötteiden lukeminen lopetetaan kokonaislukuun nolla. Rajauksia on kaksi: (1) luvut, jotka ovat suurempia tai yhtä suuria kuin kymmenen tulee rajata pois; (2) luvut, jotka ovat pienempiä tai yhtä suuria kuin nolla tulee rajata pois. Lukumäärän laskemiseen tarvitaan erikseen muuttuja. Ohjelma kokonaisuudessaan alla.

// Tuodaan Scanner-apuväline ohjelman tietoon
import java.util.Scanner;

public class Ohjelma {

    public static void main(String[] args) {
        // Luodaan lukemiseen käytettävä Scanner-apuväline
        Scanner lukija = new Scanner(System.in);

        int rajattujenLukujenLukumaara = 0;

        // lue käyttäjältä kunnes -- toistolause
        while (true) {
            // lue käyttäjältä kokonaislukuja
            int luku = Integer.valueOf(lukija.nextLine());

            // kunnes käyttäjä syöttää nollan
            if (luku == 0) {
                break;
            }

            // rajataan pois luvut, jotka ovat suurempia
            /// tai yhtä suuria kuin kymmenen
            if (luku >= 10) {
                continue;
            }

            // rajataan pois luvut, jotka ovat pienempiä
            // tai yhtä pieniä kuin nolla
            if (luku <= 0) {
                continue;
            }

            // lukemisen jälkeen tehtävä toiminnallisuus
            rajattujenLukujenLukumaara = rajattujenLukujenLukumaara + 1;
        }

        // toistolauseen jälkeen tehtävä toiminnallisuus
        System.out.println(rajattujenLukujenLukumaara);

    }
}

Alla muutamia ohjelman esimerkkitulosteita.

Esimerkkitulostus

11 4 -2 1 0 2

Esimerkkitulostus

10 0 0

:

:

Kirjaudu sisään nähdäksesi tehtävän.

Loading
Pääsit aliluvun loppuun! Jatka tästä seuraavaan osaan:

Muistathan tarkistaa pistetilanteesi materiaalin oikeassa alareunassa olevasta pallosta!