Föreläsning 4: Poster Följande är genomgånget: type Person_Type is Namn : String(30); Skonr : Float; Kon : Boolean; Diskussion runt detta med olika typer m.m. Har tagit upp vilka operationer man kan göra på en post. Tilldelning, likhet, åtkomst av delar av posten. Utvidgning: type Person_Type2 is Namn : String(30); Skonr : Float; Kon : Boolean; Barn : Person_Typ; Pratat om att man inte kan använda sig av en typ innan den är färdigdeklarerad m.m. Åtkomst av första tecknet i barnets namn m.m. Angående strängar har jag tagit upp: X := Integer'Value(Str(1..2)); D.v.s. Konvertering från sträng till heltal samt delsträngar. Attributet Value till typen Integer. Sagt att det är ovanligt att använda typer mellan "" och "end", men att detta var ett tillfälle. Övning på lektion Uppgift 1: Skapa en datatyp som motsvarar ett komplext tal på formen "A + Bi". Går att lösa med strängar, fält m.m., men vi vill ha en post. Hur gör man? Blir säkert lite frågor kring om man skall lagra "+" och "i", men detta är ju onödigt. Dessa är ju bara till för att visa för oss användare vad det är för typ av tal. Det räcker med talen "A" och "B". Vi kan använda "Float" eller "Integer". Det spelar ingen roll, men jag brukar styra det till "Integer" för att sen få det lättare vid inläsningen. Vi får förhoppningsvis fram: type Complex_Type is Re : Integer; Im : Integer;
Uppgift 2: Skriv ett program som läser in två komplexa tal och summerar dessa och skriver ut summan. Skall lösas med underprogram. De kommer säkert fram till att man skall göra något i stil med följande i huvudprogrammet. Åtminstone efter lite diskussioner fram och tillbaka. with Ada.Text_IO; use Ada.Text_IO; procedure Complex_Summary is type... Enligt ovan. A, B, Sum : Complex_Type; Put("Mata in..."); Get(A); Get(B); Sum := Add(A, B); Put("Summan blev "); Put(Sum); New_Line; end Complex_Summary; Nu måste vi dock skapa en egen "Get", en "Add" och en "Put" för att klara av detta. Vi fixar dessa genom lite diskussion med studenterna. procedure Get(Item : out Complex_Type) is Ch : Character; Get(Item.Re); Get(Ch); Tar bort '+'. (Nödvändigt?) Get(Item.Im); Get(Ch); Tar bort 'i'. end Get; Varför skall 'i' läsas bort? Varför skall detta skrivas? Dessa frågor dyker (nästan) alltid upp. Svar: Man vill givetvis mata in talet som man ser det rent matematiskt och programmet måste då se till att det läser in allt som har med ett komplext tal att göra så att nästa inmatning ("Get") kan ta nästa data utan att det ligger "skrot" kvar. Ovanstående "Get" brukar de komma fram till. Jag visar ofta en annan variant där man sätter lite hårdare begränsingar på inmatningen, men som ger lite övning på "Integer'Value" som ser ut på följande sätt.
procedure Get(Item : out Complex_Type) is Str : String(1..6); Kräver att data matas in på formen: "NN+NNi" Get(Str); Konvertering till post. Item.Re := Integer'Value(Str(1..2)); Item.Im := Integer'Value(Str(4..5)); end Get; De vet om detta och kan själva få ge tips om hur man skriver satserna för att göra de olika delarna. Ibland frågar de varför man gör på detta vis och då kan man säga att det kan finnas tillfällen då man kanske måste ge ett visst format på indata för att det skall bli lättare att lösa problemet för programmeraren, men att det i idealfallet inte borde vara så. Nu skall vi kanske skapa "Put": Det var ju inte så svårt. procedure Put(Item : in Complex_Type) is Ada.Integer_Text_IO.Put(Item.Re, Width => 2); Ada.Text_IO.Put('+'); Ada.Integer_Text_IO.Put(Item.Im, Width => 2); Ada.Text_IO.Put('i'); end Put; Vi går in på "Add". Här får de tänka lite på hur parametrar m.m. skall skickas och vad som skall returneras, men det är inga konstigheter. Behövs dock en lokal variabel. function Add(A, B : in Complex_Type) C.Re := A.Re + B.Re; C.Im := A.Im + B.Im; end Add; Nu kommer man till detta med vilka paket som programmet måste inkludera. Det saknas åtminstone "Ada.Integer_Text_IO". De kan ju få fundera på detta. Efter detta modifierar jag uppgiften och säger att jag vill ha en funktion som multiplicerar istället för adderar. Hur löser de detta? Konstigt nog har de ofta problem med detta. Låt dem hjälpa till och fundera utan att stressa dem.
function Multiply(A, B : in Complex_Type) C.Re := (A.Re * B.Re) (A.Im * B.Im); C.Im := (A.Re * B.Im) + (A.Im * B.Re); end Multiply; Nu kanske man inte vill anropa dessa funktioner ("Add" och "Mult") så som de är gjorda just nu. Man vill väl normalt sett kunna utföra en "normal" addition med "+". Hur gör vi då? Vi bara byter ut "Add" mot ""+"" (ser lite roligt ut i denna text, men...) och på motsvarande sätt för "Mult". function "+"(A, B : in Complex_Type) C.Re := A.Re + B.Re; C.Im := A.Im + B.Im; end "+"; På detta sätt kan vi anropa "funktionen" (eller operatoröver-lagringen som det egentligen heter) på följande sätt.... Sum := A + B;... end...; Mycket trevligt då detta ser ut som i matematiken. Lite magiskt, men trevligt. Uppgift 3: Skapa ett paket av det vi gjort som har med komplexa tal att göra så att vi kan använda det i ett annat program senare.
Börja med att plocka ut det som skall till "ads"-filen, d.v.s. typer och underprogramsdeklarationer (eventuellt konstanter). package Complex_Handling is type Complex_Type is Re : Integer; Im : Integer; procedure Get(Item : out Complex_Type); procedure Put(Item : in Complex_Type); function "+"(A, B : in Complex_Type) return Complex_Type; function "*"(A, B : in Complex_Type) return Complex_Type; Diskussion om att kanske ha med jämförelseoperatorer... function ">"(A, B : in Complex_Type) return Complex_Type; end Complex_Handling;
Fortsätt sen med "adb"-filen. package body Complex_Handling is procedure Get(Item : out Complex_Type) is Ch : Character; Get(Item.Re); Get(Ch); Tar bort '+'. (Nödvändigt?) Get(Item.Im); Get(Ch); Tar bort 'i'. end Get; procedure Put(Item : in Complex_Type) is Ada.Integer_Text_IO.Put(Item.Re, Width => 2); Ada.Text_IO.Put('+'); Ada.Integer_Text_IO.Put(Item.Im, Width => 2); Ada.Text_IO.Put('i'); end Put; function "+"(A, B : in Complex_Type) C.Re := A.Re + B.Re; C.Im := A.Im + B.Im; end "+"; function "*"(A, B : in Complex_Type) C.Re := (A.Re * B.Re) (A.Im * B.Im); C.Im := (A.Re * B.Im) + (A.Im * B.Re); end "*"; end Complex_Handling;
Hur gör man för att använda detta paket från huvudprogrammet är nästa fråga. Skriv ett huvudprogram som klarar biffen. with Ada.Text_IO; use Ada.Text_IO; with Complex_Handling; use Complex_Handling; procedure Complex_Summary is A, B, Sum : Complex_Type; Put("Mata in..."); Get(A); Get(B); Sum := A + B; Put("Summan blev "); Put(Sum); New_Line; end Complex_Summary; Lite illa gjort paket fortfarande: Vi vill nog ha datatypen privat. Hur gör vi då? Vi "rättar" bara i "ads"-filen. Inget händer någon annanstans. package Complex_Handling is type Complex_Type is private; procedure Get(Item : out Complex_Type); procedure Put(Item : in Complex_Type); function "+"(A, B : in Complex_Type) return Complex_Type; function "*"(A, B : in Complex_Type) return Complex_Type; private type Complex_Type is Re : Integer; Im : Integer; end Complex_Handling; Vad innebär detta för oss i paketet? Vad innebär det för den som skriver huvudprogrammet? Bra frågor som ni bör diskutera (även om jag gjort det på FÖ redan). I paketet innebär det att vi slipper göra felkontroller på de data som ligger i vår "datatyp" (eller rättare sagt i de olika variabler som kommer in till våra rutiner). I huvudprogrammet innebär det att det blir otillåtet att t.ex. försöka ändra variabler av vår datatyp på annat sätt än genom att anropa våra rutiner i paketet. Detta leder till att programmeraren som skriver huvudprogrammet inte kan ställa till det för oss (och sig själv i slutänden).
I vissa fall kan det dock vara trevligt att inte göra en typ privat. Det kan ju vara så att man vill att huvudprogrammet skall kunna påverka datat utan att behöva använda sig av de rutiner som finns i paketet. Om det är så får man dock hantera de eventuella situationer som kan uppstå när man skickar in data till rutinerna så att man inte får programmet att "krascha". Man bör dock tänka igenom detta noggrant innan man sätter en typ helt synlig så att man inte skapar sig en massa problem framöver. Finns det tid över kan det vara trevligt att leka med "<". Om man gör denna "korrekt" krävs att man har tillgång till både "**" och "Sqrt". Finns i "Ada.Numerics.Elementary_Functions" (eller i "Ada.Numerics.Generic_Elementary_Functions" om man vill leka med instansiering :-). Jämförelsen "<" bygger på beloppet av det komplexa talet och alla vet ju hur man räknar ut detta (ingår i "grunken" har jag för mig :-). Man kan dock fuska lite och skippa "Sqrt" då det räcker att jämföra de båda kvadraterna, men då blir det inte lika roligt längre. Dock kan det vara på sin plats att vid detta tillfälle ta upp att det är ett sådant läge där man skriver en extra kommentar i koden som anger att man har gjort saker som gör koden mer effektiv och hur man gjort detta.