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:
/** * Ohjelmalla testataan 2. asteen polynomin juurien etsimistä * @author Vesa Lappalainen * @version 1.0, 16.02.2003 */ public class P2_2 { public static void testi(double a, double b, double c) { Polynomi2 p = new Polynomi2(a,b,c); System.out.print("Polynomi: " + p); if ( p.getReaalijuuria() <= 0 ) { System.out.println(" Ei yhtään reaalijuuria! "); return; } System.out.print(" juuret: "); System.out.print("x1 = " + p.getX1() + " => P(x1) = " + p.f(p.getX1()) ); System.out.print(" ja "); System.out.print("x2 = " + p.getX2() + " => P(x2) = " + p.f(p.getX2()) ); System.out.println(); } public static void main(String[] args) { testi(1,2,1); testi(2,1,0); testi(1,-2,1); testi(2,-1,0); testi(2,1,1); testi(2,0,0); testi(0,2,1); testi(0,0,1); } } /** * Luokka toisen asteen polynomille ja sen nollakohdille * @author Vesa Lappalainen * @version 1.0, 16.02.2003 */ class Polynomi2 { private double a,b,c,x1,x2; private int reaalijuuria; public Polynomi2(double a, double b, double c) { this.a = a; this.b = b; this.c = c; reaalijuuria = ratkaise_2_asteen_yhtalo(); } private int ratkaise_2_asteen_yhtalo() { double D,SD; x1 = x2 = 0; if ( a==0 ) { /* bx + c = 0 */ if ( b==0 ) { /* c = 0 */ if ( c==0 ) { /* 0 = 0 */ return 1; /* id. tosi */ } /* c==0 */ else { /* c!=0 */ /* 0 != c = 0 */ return 0; /* Aina epät. */ } /* c!=0 */ } /* b==0 */ else { /* b!=0 */ /* bx + c = 0 */ x1 = x2 = -c/b; return 1; } /* b!=0 */ } /* a==0 */ else { /* a!=0 */ /* axx + bx + c = 0 */ D = b*b - 4*a*c; if ( D>=0 ) { /* Reaaliset juuret */ SD = Math.sqrt(D); x1 = (-b-SD)/(2*a); x2 = (-b+SD)/(2*a); return 2; } /* D>=0 */ else { /* Imag. juuret */ return -1; } /* D<0 */ } /* a!=0 */ } public static double P2(double x, double a, double b, double c) { return (a*x*x + b*x + c); } public double f(double x) { return P2(x,a,b,c); } public double getX1() { return x1; } public double getX2() { return x2; } public int getReaalijuuria() { return reaalijuuria; } public String toString() { return a + "x^2 + " + b + "x + " + c; } }
Edellinen metodi ratkaise_2_asteen_yhtalo 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:
private int ratkaise_2_asteen_yhtalo() { double D,SD; x1 = x2 = 0; if ( a == 0 ) if ( b == 0 ) { if ( c == 0 ) return 1; else return 0; } else { x1 = x2 = -c/b; return 1; } else { D = b*b - 4*a*c; if ( D >= 0 ) { SD = Math.sqrt(D); x1 = (-b-SD)/(2*a); x2 = (-b+SD)/(2*a); return 2; } else return 0; } }
Joskus kannattaa harkita olisiko luettavuuden kannalta paras esitystapa sellainen, että käsitellään "normaaleimmat" tapaukset ensin:
private int ratkaise_2_asteen_yhtalo() { double D,SD; x1 = x2 = 0; if ( a != 0 ) { D = b*b - 4*a*c; if ( D >= 0 ) { SD = Math.sqrt(D); x1 = (-b-SD)/(2*a); x2 = (-b+SD)/(2*a); return 2; } else return -1; } else /* a==0 */ if ( b != 0 ) { x1 = x2 = c/b; return 1; } else { /* a==0, b==0 */ if ( c == 0 ) return 1; else return 0; } }
Usein aliohjelman return- lauseen ansiosta else osat voidaan jättää poiskin:
private int ratkaise_2_asteen_yhtalo() { double D,SD; x1 = x2 = 0; if ( a == 0 ) { if ( b == 0 ) { if ( c == 0 ) return 1; return 0; } x1 = x2 = -c/b; return 1; } D = b*b - 4*a*c; if ( D < 0 ) return -1; SD = Math.sqrt(D); x1 = (-b-SD)/(2*a); x2 = (-b+SD)/(2*a); return 2; }
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 true; else return false;
voitaisiin korvata rakenteella
return ( c != 0 );