2.7.1 Toistolause while

Silmukalla while saadaan ohjelma suorittamaan toimenpidettä niin kauan kuin annettu ehto on voimassa. Silmukan yleinen muoto on

while (ehto) lause;

Lauseen suoritus alkaa ehto-lausekkeen laskemisella. Jos tulos on tosi (nollasta poikkeava), suoritetaan lause. Sen jälkeen ehto lasketaan uudelleen, ja jos tulos on jälleen tosi, suoritetaan lause, jne. Sitten kun ehdon tulos on epätosi (nolla), lause:tta ei enää suoriteta, vaan while-lauseen suoritus päättyy.

Myös tässä voidaan lause korvata lohkolla, jolloin silmukassa suoritetaan useita lauseita:

while (ehto) {
  lause1;
  lause2;
  ...
}

On huomattava, että silmukan suoritusosan (lause tai lohko) suoritusten määrä riippuu ainoastaan lausekkeesta ehto. Silmukassa

while (0) cout << "Kukkuu!";

ei suoritusosaa suoriteta lainkaan, sillä ehto on alusta alkaen epätosi (kokonaislukuarvo 0). Sen sijaan

while (1) cout << "Kukkuu!";

tulostaa sanaa Kukkuu aina tuomiopäivään saakka, tai ainakin niin kauan kuin virtaa riittää (ikuinen silmukka), sillä ehto on aina tosi (nollasta poikkeava kokonaislukuarvo). Jos ohjelmaan eksyy ikuinen silmukka, voidaan yrittää ohjelman suorituksen keskeyttämistä näppäilemällä Ctrl+Break.

Yleensä ottaen kannattaa pitää huoli siitä, että ehtolausekkeen arvoa päivitetään silmukan suorituksien aikana. Esimerkiksi seuraava ohjelmanpätkä tulostaa kymmenen kertaa tekstin Moi! kuvaruudulle:

lkm = 1;
while (lkm < 11) {
  cout << "Moi!" << endl;
  lkm++;
}

Silmukan suoritusosassa kasvatetaan laskuria lkm, joten voidaan luottaa siihen, että muuttujan lkm arvo ylittää joskus arvon 10. Tällöin ehtolausekkeen arvoksi tulee epätosi ja silmukan suoritus päättyy.

Esimerkki

Oletetaan, että Cooperin testi (= 12 minuuttia hillitöntä juoksemista) suoritetaan urheilukentällä, jonka radan pituus on 400 metriä. Tavoitteena olisi tehdä ohjelma, joka kysyy kuinka monta metriä henkilö ehti juosta testin aikana. Metrimäärän perusteella ohjelma laskee juostujen kierrosten lukumäärän. (Tämä olisi tietenkin helpointa tehdä suoraan jakolaskulla, mutta nyt harjoitellaankin silmukan käyttöä).

/* *********************************************************
COOPER1.CPP
  Laskee juostut (täydet) kierrokset Cooperin testissä,
  kun käyttäjä antaa juoksemansa matkan metreissä.
********************************************************* */

#include <iostream.h>

#define RADAN_PITUUS 400 // juoksuradan pituus metreissä

void tulosta_esittely(void)
{
  cout << "\n\n";
  cout << "Cooperin testi\n" << endl;
}

void kysy_matka(int &matka)
{
  cout << "Anna juoksemasi matka metreinä > ";
  cin >> matka;
}

int laske_kierrokset(int &matka)
{
  int kierrokset = 0;

  /* silmukan suoritus loppuu sitten, kun matka-muuttujan
     arvo on pienempi kuin radanpituus                    */
  while (matka >= RADAN_PITUUS) {
    matka = matka - RADAN_PITUUS;
    kierrokset++;
  }

  return kierrokset;
}

void tulosta_kierrokset(int kierrokset, int loppumatka)
{
  cout << "\n";
  cout << "Juoksit " << kierrokset << " kierrosta ";
  cout << "ja lisäksi " << loppumatka << " metriä." << endl;
}

int main(void)
{
  int matka;       // juostu matka metreissä
  int kierros_lkm; // juostut kokonaiset ratakierrokset

  tulosta_esittely();
  kysy_matka(matka);
  kierros_lkm = laske_kierrokset(matka);
  tulosta_kierrokset(kierros_lkm, matka);

  return 0;
}

Esimerkkiajo:

Cooperin testi

Anna juoksemasi matka metreinä > 3260

Juoksit 8 kierrosta ja lisäksi 60 metriä.

Miten ohjelma toimii, jos käyttäjä antaakin negatiivisen arvon? Entäpä, jos käyttäjä liioittelee saavutustaan ja antaa arvoksi 33000?

Negatiivinen arvo aiheuttaa silmukan suorittamatta jättämisen ja arvolla 33000 muuttujassa matka tapahtuu ylivuoto. Näistä ongelmista päästää eroon lisäämällä matkan kysymiskohtaan uusi while-lause, josta ei poistuta ennenkuin käyttäjä on antanut järkevän arvon. Samalla nämä raja-arvot on laitettu symbolisiksi vakioiksi.

/* *********************************************************
COOPER2.CPP
  Laskee juostut (täydet) kierrokset Cooperin testissä.
  Käyttäjä antaman matkan järkevyys tarkistetaan.
********************************************************* */

#include <iostream.h>

#define RADAN_PITUUS 400
#define MAKSIMI 6000     // annetun matkan yläraja
#define MINIMI 100       // annetun matkan alaraja

void tulosta_esittely(void)
{
  cout << "\n\n";
  cout << "Cooperin testi\n" << endl;
}

void kysy_matka(int &matka)
{
  cout << "Anna juoksemasi matka metreinä > ";
  cin >> matka;
  /* toistetaan kunnes annettu matka on järkevä */
  while (matka < MINIMI || matka > MAKSIMI) {
    cout << "\n";
    cout << "Antamasi matka (" << matka << " m) on järjetön!\n";
    cout << "Anna juoksemasi matka metreinä > ";
    cin >> matka;
  }
}

int laske_kierrokset(int &matka)
{
  int kierrokset = 0;

  while (matka >= RADAN_PITUUS) {
    matka = matka - RADAN_PITUUS;
    kierrokset++;
  }

  return kierrokset;
}

void tulosta_kierrokset(int kierrokset, int loppumatka)
{
  cout << "\n";
  cout << "Juoksit " << kierrokset << " kierrosta ";
  cout << "ja lisäksi " << loppumatka << " metriä." << endl;
}

int main(void)
{
  int matka;
  int kierros_lkm;

  tulosta_esittely();
  kysy_matka(matka);
  kierros_lkm = laske_kierrokset(matka);
  tulosta_kierrokset(kierros_lkm, matka);

  return 0;
}

Nyt käyttäjä pakotetaan antamaan jokin arvo 100 ja 6000 metrin väliltä ennen kuin ohjelmassa päästään eteenpäin:

Cooperin testi

Anna juoksemasi matka metreinä > 0

Antamasi matka (0 m) on järjetön!
Anna juoksemasi matka metreinä > 6100

Antamasi matka (6100 m) on järjetön!
Anna juoksemasi matka metreinä > 4500

Juoksit 11 kierrosta ja lisäksi 100 metriä.