Ylös Edellinen Seuraava Otsikkosivu Hakemisto Sisällys

10.5.1 Sisäkkäiset if- lauseet

Meillä oli aikaisemmin tehtävänä kirjoittaa funktio, joka palauttaa toisen asteen yhtälön ax2+bx+c=0 toisen juuren. Tällöin oletuksena oli, että a<>0 ja D>=0. Mikäli ratkaisukaavaa sovelletaan sellaisenaan ja a=0 tai D<0, niin tällöin ohjelman suoritus päättyy ajonaikaiseen virheeseen.

Voisimme muuttaa tehtävän määrittelyä siten, että kumpikin juuri pitää palauttaa ja funktion nimessä palautetaan tieto siitä, tuliko ratkaisussa virhe, eli jollei juuret olekaan reaalisia.

	if ( a != 0 ) {
	  D = b*b -  4*a*c;
	  if ( D > 0 ) {
	    ...
	  }
	  else {
	    ...
	  }
	}
	else {
	  ...
	}

Tosin yhtälö pystytään mahdollisesti ratkaisemaan myös kun a==0. Tällöin tehtävä jakautuu useisiin eri tilanteisiin kertoimien a,b ja c eri kombinaatioiden mukaan:


juuret

a
b
c
D

yhtälön muoto
reaalisia
x1
x2

0
0
0
?

0 = 0
juu
0
0

0
0
c
?

c = 0
ei
0
0

0
b
?
?

bx - c = 0
juu
-c/b
-c/b

a
?
?
>=0

ax 2 + bx + c = 0
juu
(-b-SD)/2a
(-b+SD)/2a

a
?
?
<0

- " -
ei

Algoritmiksi kirjoitettuna tästä seuraisi:

	1.  Jos a=0, niin 
	      Jos b=0
	         Jos c=0 yhtälö on muotoa 0=0 joka on aina tosi
	           palautetaan vaikkapa x1=x2 =0
	         muuten (eli c<>0) yhtälö on muotoa c=0 joka on
	           aina epätosi, palautetaan virhe
	      muuten (eli b<>0) yhtälö on muotoa bx=c 
	          joten voidaan palauttaa vaikkapa x1=x1=- c/b
	2.  Jos a<>0, niin
	      Jos D>=0 kyseessä aito 2. asteen yhtälö ja käytetään
	        ratkaisukaavaa
	      muuten (eli D<0) ovat juuret imaginaarisia

Funktio ja sen testiohjelma voisi olla esimerkiksi seuraavanlainen:

c-silm\p2_2.cpp - esimerkki 2. asteen yhtälön ratkaisemiseta

	#include <iostream.h>
	#include <math.h>
	
	int ratkaise_2_asteen_yhtalo(double &x1, double &x2,
	                             double a, double b, double c)
	{
	  double D,SD;
	  x1 = x2 = 0;
	  if ( a == 0 ) {                     /*       bx + c = 0 */
	    if ( b == 0 ) {                   /*            c = 0 */
	      if ( c == 0 ) {                 /*            0 = 0 */
	        return 0;                     /* id. tosi         */
	      }                 /* c==0 */
	      else {            /* c!=0 */    /*       0 != c = 0 */
	        return 1;                     /* Aina epät.       */
	      }                 /* c!=0 */
	    }                   /* b==0 */
	    else {              /* b!=0 */    /*       bx + c = 0 */
	      x1 = x2 = - c/b;
	      return 0;
	    }                   /* b!=0 */
	  }                     /* a==0 */
	  else {                /* a!= 0 */   /* axx + bx + c = 0 */
	    D = b*b -  4*a*c;
	    if ( D >= 0 ) {                   /* Reaaliset juuret */
	      SD  = sqrt(D);
	      x1 = (- b- SD)/(2*a);
	      x2 = (- b+SD)/(2*a);
	      return 0;
	    }                   /* D>=0 */
	    else {                            /* Imag. juuret     */
	      return 1;
	    }                   /* D<0  */
	  }                     /* a!=0 */
	}
	
	double P2(double x, double a, double b, double c)
	{
	  return (a*x*x + b*x + c);
	}
	
	int main(void)
	{
	  double a,b,c,x1,x2;
	  do {
	    cout << "Anna 2. asteen yhtälön a b c >";
	    cin >> a >> b >> c;
	    if ( ratkaise_2_asteen_yhtalo(x1,x2,a,b,c) ) {
	      cout << "Yhtälöllä ei ole reaalisia juuria!" << endl;
	    }
	    else {
	      cout << "1. ratkaisu on "  << x1
	           << ".  Arvoksi tulee tällöin " << P2(x1,a,b,c) << endl;
	      cout << "2. ratkaisu on "  << x2
	           << ".  Arvoksi tulee tällöin " << P2(x2,a,b,c) << endl;
	    }
	  } while (a>0);
	  return 0;
	}

Edellinen funktio on äärimmäinen esimerkki sisäkkäisistä if- lauseista. Jälkeenpäin sen luettavuus on erittäin heikko ja myös kirjoittaminen hieman epävarmaa. Parempi kokonaisuus saataisiin lohkomalla tehtävää pienempiin osasiin aliohjelmien tai makrojen avulla.

Sisäkkäisten if- lauseiden kirjoittamista voidaan helpottaa kirjoittamalla niitä sisenevästi, eli aloittamalla ensin tekstistä:

	  if ( a == 0 ) {                     /*       bx + c = 0 */
	  }                     /* a==0 */
	  else {                              /* axx + bx + c = 0 */
	    D = b*b -  4*a*c;
	  }                     /* a!=0 */

Sitten täydennetään vastaavalla ajatuksella sekä if- osan että else- osan toiminta.

Jos funktiosta karsitaan kaikki ylimääräinen (kommentit ja ylimääräiset lausesulut) pois, saamme seuraavan näköisen kokonaisuuden:

c-silm\p2_2l.cpp - karsittu versio 2. asteen yhtälöstä

	int ratkaise_2_asteen_yhtalo(double &x1, double &x2,
	                             double a, double b, double c)
	{
	  double D,SD;
	  x1 = x2 = 0;
	  if ( a == 0 )
	    if ( b == 0 ) {
	      if ( c == 0 ) return 0;
	      else return 1;
	    }
	    else {
	      x1 = x2 = - c/b;
	      return 0;
	    }
	  else {
	    D = b*b -  4*a*c;
	    if ( D >= 0 ) {
	      SD  = sqrt(D);
	      x1 = (- b- SD)/(2*a);
	      x2 = (- b+SD)/(2*a);
	      return 0;
	    }
	    else return 1;
	  }
	}

Joskus kannattaa harkita olisiko luettavuuden kannalta paras esitystapa sellainen, että käsitellään "normaaleimmat" tapaukset ensin:

c-silm\p2_2n.cpp - normaalit tapaukset ensin ratkaisussa

	int ratkaise_2_asteen_yhtalo(double &x1, double &x2, 
	                             double a, double b, double c)
	{
	  double D,SD;
	  x1 = x2 = 0;
	  if ( a != 0 ) {
	    D = b*b -  4*a*c;
	    if ( D >= 0 ) {
	      SD  = sqrt(D);
	      x1 = (- b- SD)/(2*a);
	      x2 = (- b+SD)/(2*a);
	      return 0;
	    }
	    else return 1;
	  }
	  else /* a==0 */
	    if ( b != 0 ) {
	      x1 = x2 = - c/b;
	      return 0;
	    }
	    else { /* a==0, b==0 */
	      if ( c == 0 ) return 0;
	      else return 1;
	    }
	}

Usein aliohjelman return- lauseen ansiosta else osat voidaan jättää poiskin:

c-silm\p2_2r.cpp - else -osat pois

	int ratkaise_2_asteen_yhtalo(double &x1, double &x2, 
	                             double a, double b, double c)
	{
	  double D,SD;
	  x1 = x2 = 0;
	  if ( a == 0 ) {
	    if ( b == 0 ) {
	      if ( c == 0 ) return 0;
	      return 1;
	    }
	    x1 = x2 = - c/b;
	    return 0;
	  }
	
	  D = b*b -  4*a*c;
	  if ( D < 0 ) return 1;
	
	  SD  = sqrt(D);
	  x1 = (- b- SD)/(2*a);
	  x2 = (- b+SD)/(2*a);
	  return 0;
	}

Edellä oli useita eri ratkaisuja saman ongelman käsittelemiseksi. Liika kommenttien määrä saattaa myös sekoittaa luettavuutta kuten 1. esimerkissä. Toisaalta liian vähillä kommenteilla ei ehkä kirjoittaja itsekään muista jälkeenpäin mitä tehtiin ja miten. Jokainen valitkoon edellä olevista itselleen sopivimman kultaisen keskitien.

Huomattakoon vielä lopuksi, että rakenne

	if ( c == 0 ) return 0;
	else return 1; 

voitaisiin korvata rakenteella

	return ( c != 0 ); 

Tehtävä 10.97 else - osat pois

Kirjoita ratkaise_2_asteen_yhtalo /* p2_2n.c */ ilman else - osia.

Ylös Edellinen Seuraava Otsikkosivu Hakemisto Sisällys