Laboration nr 2 del 2, En klass för Rationella tal (3p) 2013

Inledning

Testar: att göra egna klasser och metoder. Avsikten med laborationen är att du skall öva på grundläggande objektorienterade begrepp såsom klasser, objekt, instansmetoder, instansvariabler och konstruktorer. Även saker som uttryck och klassen String dyker upp. (Denna laboration bör du klara av efter att ha läst om att göra egna klasser och metoder.)

Uppgiften är att konstruera en klass RatNum som beskriver rationella tal.  Ett rationellt tal uttrycks som bekant som kvoten mellan två heltal. Vi skriver rationella tal på formen 1/3, -1/5.

För att förenkla arbetet har laborationen delats in i tre steg. När du fått ett steg färdigt bör det, om så är möjligt, visas upp för en handledare.
När
steg 3 är klart skall laborationen lämnas in i fire.

Dokumentation.

Dokumentationen skall innehålla programtexten för din klass RatNum samt i README filen den utskrift programmet RatNumTest3 producerar när det körs med filen indata.txt som indata. I dokumentationen skall du också med egna ord ge en beskrivning av vad det färdiga huvudprogrammet RatNumTest3 gör och hur det fungerar.  Det viktiga är att övergripande beskriva hur programmet gör vad det gör, inte bara en översättning till talspråk tex. "här deklareras en varibel sc...". men det räcker inte heller med att säga att "det testar att toDouble fungerar" utan du skall beskriva lite hur det går till.

Programmet skall utformas enligt de principer som lärts ut i kursen och koden skall vara indenterad mm enligt stilguiden på hemsidan. För att bli godkänd räcker det alltså inte med att programmet fungerar korrekt, det måste vara välstrukturerat och läsbart också!

Steg 1

När man beskriver rationella tal är det bäst att förkorta bort alla gemensamma faktorer i täljaren och nämnaren. För att kunna göra denna förkortning behövs en klassmetod som beräknar den största gemensamma divisorn till två heltal m och n. Skriv en sådan metod! Kalla metoden gcd (greatest common divisor, se wikipedia för mer information om gcd men kopiera inte kod från nätet!) och placera den i en klass med namnet RatNum.
Metoden ska acceptera både positiva och negativa argument som inte båda är lika med noll, men den ska alltid ge ett resultat som är större än noll. Utforma din metod så att den genererar en exception av typen IllegalArgumentException om bägge parametrarna är lika med noll. Detta kan göras med satsen
   throw new IllegalArgumentException();

Tips. Euklides algoritm för att beräkna den största gemensamma divisorn till två positiva heltal m och n kan beskrivas på följande sätt:

- Dividera m med n och beteckna resten vid divisionen med r.
- Om r=0 så är beräkningen klar och resultatet finns i n.
- Sätt annars m till n och n till r och gå tillbaka till steg 1.

Denna algoritm fungerar om talen  m och n båda är >=1. Se därför i metoden gcd till att parametrarna alltid görs positiva innan algoritmen tillämpas.

Om något argument är noll fungerar inte algoritmen ovan utan då gäller: (dessa fall kallas gränsfall eller randvillkor och att man inte gör rätt på dom är en vanlig källa till fel)
gcd(a, 0) = |a|, för a ≠ 0 "since any number is a divisor of 0, and the greatest divisor of a is |a|". (|a| är absolutbeloppet av a)
gcd(a, b) = gcd(b, a), dvs gcd(0, a) bör också vara |a|
gcd(a, a) = |a|, men det gäller bara när a != 0 (detta skall algoritmen ovan klara såklart)
gcd(0, 0) = vilket tal som helst !=0 delar 0 så det finns egentligen ingen gcd. Ibland säger man att gcd(0, 0) = 0. Men vi tillåter inte detta fall utan kastar en exception, se ovan.

Test.

Testa metoden gcd genom att kopiera det färdiga huvudprogrammet RatNumTest1  till samma bibliotek som klassen RatNum . Kompilera och kör programmet!

Steg 2

Det är nu dags att börja fylla i klassen RatNum så att den kan användas för att beskriva rationella tal. För varje objekt av klassen RatNum skall täljaren och nämnaren lagras som två heltal. Dessa skall alltid sakna gemensamma faktorer, dvs. ett rationellt tal skall alltid vara avkortat så långt det går. Nämnaren måste alltid vara större än noll. Negativa rationella tal representeras med en negativ täljare.

I detta steg skall du förse klassen RatNum med följande konstruktorer och metoder:

Tips.

 I den tredje konstruktorn måste du först  kontrollera att nämnaren inte är lika med 0. Om så inte är fallet så ska du dividera de båda parametrarna a och b med den största gemensamma divisorn. Du finner lätt den största gemensamma divisorn genom att använda klassmetoden gcd från steg 1. En liten komplikation är att någon eller båda av parametrarna a och b kan vara mindre än noll. Om båda är negativa betecknar a/b ett positivt tal och man kan därför ersätta a och b med sina motsvarande positiva värden. Om den ena parametern är positiv och den andra negativ betecknar ett a/b negativt tal. Man skall då låta täljaren vara negativ och nämnaren positiv.

Test.

Testa klassen RatNum genom att kopiera det färdiga huvudprogrammet RatNumTest2 till samma bibliotek som klassen RatNum. Kompilera och kör programmet! Läs sedan programkoden för huvudprogrammet RatNumTest2 och förvissa dig om att du förstår vad detta program gör.

Steg 3

Klassen skall nu utökas med några metoder, vilka förenklar inläsning och utskrift av rationella tal och gör det möjligt att utföra matematiska operationer på rationella tal. Följande metoder skall läggas till:

Tips.

I metoderna add, sub, mul och div använder du förstås dina matematiska kunskaper. Uttrycket a/b+c/d kan t.ex. skrivas om som (ad+bc)/bd. Använd en lämplig konstruktor när du skapar det tal som skall innehålla resultatet av den matematiska operationen. Då kommer automatiskt alla gemensamma faktorer att divideras bort.

I metoden parse har du stor nytta av några av de metoder för klassen String som finns i Javas API. Ett litet tips: Om parametern s till parse inte innehåller någon nämnare kan du själv lägga till texten "/1" sist i s. På det sättet behöver du inte konstruera någon speciell kod för detta fall. Använd metoden Integer.parseInt för att avkoda täljaren respektive nämnaren. Denna genererar som du vet själv en exception av typen NumberFormatException om texten inte innehåller ett heltal.

Test.

När klassen RatNum är klar skall den testas tillsammans med det färdiga huvudprogrammet RatNumTest3. Studera detta program och sätt dig in i hur det fungerar! Det är upplägget som är viktigast, inte detaljerna men det är lärorikt att förstå även dem. När man kör det färdiga huvudprogrammet tillsammans med din klass RatNum kan det se ut på följande sätt:

Skriv uttryck på formen a/b ? c/d, där ? är något av tecknen + - * / = <

> 1/3 + 1/5

1/3 + 1/5       --> 8/15

> 2/3 * 2/5

2/3 * 2/5       --> 4/15

> 1/3 - 2/5

1/3 - 2/5       --> -1/15

> 2/3 / 2/5

2/3 / 2/5       --> 1 2/3

> -2/3 - 2/5

-2/3 - 2/5      --> -1 1/15

> 2/11 < 1/5

2/11 < 1/5      --> true

> 3/15 = 1/5

3/15 = 1/5      --> true

> 5 / 2/3

5 / 2/3 --> 7 1/2

> 5/9 * 2

5/9 * 2 --> 1 1/9

>

För att underlätta testningen av klassen RatNum finns det en färdig fil indata.txt som du kan använda om du vill. Du kör den genom att i ett kommandoönster ge kommandot

java RatNumTest3 < indata.txt

Utskriften som programmet då ger kan jämföras med "facit" som finns i filen utdata.txt. När programmet är färdigt skall du köra filen indata.txt som indata till programmet.

Vanliga fel tidigare år

- lessThan: att bara omvandla till double och jämföra kan ge fel svar eftersom flyttal inte lagras exakt.
- "public boolean equals( RatNum r )" är fel signatur.
equals signatur *skall* se ut på ett viss sätt med Objekt som parameter och metoden skall skrivas på ett viss sätt. Man kan skriva med eller utan hänsyn till arv men i bägge fallen måste man ta hänsyn till null och testa att klassen för this och parametern är lika. (Vi har antagligen inte pratat om equals när ni lämnar in labben så innehållet är ett mindre fel på den här labben. Signaturen skall dock stämma.)

- Man har *aldrig* en catch sats med tom hanterare, tag bort. (Utom möjligen när man testar)
- Man fångar inte klassen Exception (för den fångar allt) utom möjligen när man testar.
Om ni inte har koll på exceptions (och det har ni antagligen inte än) så använd dom inte annat än för att "kasta".


This document was last changed 2013-09-06 klockan 00:17 by Erland Holmström, mail: erland please add @chalmers.se