Föreläsning 3.2: Fält Antag att man har ett antal variabler med heltal som skall innehålla data som hör väldigt intimt ihop. T.ex. om man skall hålla reda på hur många slag man slagit på respektive bana i golf. Det som krävs för detta är 18 stycken variabler och om vi vill anropa ett underprogram för att beräkna totalt antal slag för denna omgång måste man skicka 18 parametrar för att kunna utföra beräkningen. Rita en figur som visar 18 heltalsvariabler som vi döper till A1, A2, ..., A18. Programkoden som motsvarar deklarationen av dessa 18 variabler: with ...; use ...; procedure Xxx is A1, A2, ..., A18 : Integer; begin ... end Xxx; Utöka sen programmet med satser så att alla varablerna skall få värdet 4. Kan göras vid deklarationen med EN tildelning (alla variablerna får då samma värde), men antag att man senare skall tilldela alla variablerna värdet 5. Vad göra då? Jo, man måste göra 18 tilldelningar. Det vore bättre om man kunde "klumpa ihop" dessa 18 variabler till en, men ändå kunna bearbeta varje data var för sig om det behövs. I matematiken har vi något som kallas vektorer och matriser för att hålla ihop data som hör intimt ihop. Rita en figur som visar hur det skulle kunna se ut. A 1 2 18 I programspråk inför man oftast någon motsvarighet till detta med matriser (vektorn är ju bara en endimesionell matris) och i Ada har man något som kallas för "fält" (eng. "array"). Vi ser detta med fält som en ny datatyp och för att deklarera en variabel som den ovan gör vi på följande sätt. with ...; use ...; procedure Xxx is A, B : array (1..18) of Integer; begin ... end Xxx; I matematiken använder man variabelns värde och ett "index" för att ange vilken del av variabeln som refereras. Vi gör på samma sätt, men för att ange ett index markerar vi detta genom att sätta det inom parentes efter variabeln. Antag att vi vill tilldela position (eller index) 1 i fältet värdet 4. I Ada ser det ut på följande sätt. begin A(1) := 4; end Xxx; Vi kan alltså göra exakt samma saker som med de 18 variablerna A1-A18 i första exemplet, men vi måste skriva parenteser runt det som markerar index. Detta kanske bara låter som om vi får skriva mer kod utan att tjäna något på det, men det finns en del andra saker som vi kan utföra som är omöjliga att göra om man har flera variabler. Antag att vi vill tilldela alla 18 variablerna i exempel 1 värdet 0. För att göra detta måste vi skriva 18 tilldelningar. Någon kanske ser att det går att göra direkt i deklarationen genom att ge ett initialvärde till alla variablerna samtidigt. Jo, det är sant att det går, men antag att man senare vill sätta värdet 2 i alla variabler. Då är vi tillbaka till tilldelningarna igen. Det finns flera sätt att tilldela alla positioner i ett fält samma värde (eller till och med olika värden i vissa fall) utan att behöva skriva 18 tilldelningar. Vi tar några olika varianter (inklusive 18 tilldelningar). Alt 1: Precis som med 18 variabler. A(1) := 0; A(2) := 0; A(3) := 0; ... A(18) := 0; Alt 2: Tilldela hela vektorn specifika värden. A := (0, 0, 0, 0, 0, 0, 0, 0, 0, 0); A := (1, 2, 3, 8, 4, 2, 3, 54, 2, 9); Alt 3: Index 1 och 3 får värdet -1 och index 4 t.o.m. 6 får värdet 100 samt resten får värdet 0. A := (1 | 3 => 1, 4 .. 6 => 100, others => 0); Alt 4: Alla index får värdet 0. A := (1 .. 18 => 0); A := (others => 0); Alt 5: En slinga som gör arbetet. for I in 1..18 loop A(I) := 0; end loop; Alternativ 4 och 5 är de alternativ som är vanligast. Vi tittar på vilka operationer som är tillåtna att utföra på fälten vi deklarerat (A och B). begin A(1) := 4; Get(A(1)); B(1) := A(3); Put(A); ERROR!!! Finns ingen Put för fält. B := A; end ...; ERROR!!! Ej samma datatyp. Man kan fundera på varför man får kompileringsfelet när man försöker kopiera A till B ovan. Det beror på att man inte anser att A och B har samma datatyp. A och B anses i detta fall ha lika datatyper, men ej samma. För att ha samma datatyp på två variabler måste de deklareras med en enda identifierare (inte med en komplex beskrivning). Man brukar normalt sett inte skriva deklarationen av variabeln A och B på ovanstående sätt. När man använder mer komplexa datatyper (t.ex. "array (...) of ...") brukar man oftast deklarera (kallas ibland definiera) en datatyp som man själv namnger. Det ger dessutom bättre läsbarhet av koden om man ger typen ett relevant namn. Vi tittar endast på deklarationen. type Golf_Result_Type is array (1..18) of Integer; A, B : Golf_Result_Type; Deklarationen av datatypen betyder bara att när man använder identifieraren Golf_Result_Type är det en "array (1..18) of Integer" man menar. Det reserverade ordet "type" används för att ange att det som följer är ett av programmeraren påhittat namn på en datatyp. Efter det reserverade ordet "is" kommer definitionen av vad den nya typen egentligen är. På detta sätt kan man direkt se att variabeln A innehåller golfresultat och kan då lättare förstå koden som utför saker på variabeln (dessutom vore det ännu bättre om man namngav variabeln med något som talar om vad den innehåller). Dessutom bli det tillåtet att direkt kopiera (tilldela) A till B för nu har de samma datatyp. Strängar är också en typ av fält även om de behandlas lite speciellt. Här följer en deklaration av en strängvariabel och några enkla tilldelningar för att visa hur strängar fungerar. procedure Xxx is Str : String(1..5); begin Str := "Kalle"; Måste vara lika långa strängar! Str(1) := 'N'; Ersätt 'K' med 'N'. end Xxx; Rita även figur som visar strängen (med fem tecken) ser ut. Vi ritar den horisontellt då strängar normalt används för lagring av ord och meningar. Str: 'N' 'a' 1 2 'l' 3 'e' 'l' 4 5 Det är viktigt att veta skillnaden mellan 'N' (tecknet N) och "N" (strängen N). En sträng lagras på lite annat sätt i minnet än ett tecken skulle man kunna säga. En sträng lagras i vissa språk med information om hur många tecken som ingår i strängen och i andra språk med ett avslutande tecken. Däremot ett tecken är alltid bara ett tecken. Man kan fundera varför deklarationen av variabeln Str inte ser ut som deklarationen av variabeln A tidigare. Det beror på att man i Ada har fördefinierat en datatyp speciellt för strängar beroende på att dessa används ofta och är lite speciella. Hur gör man då en deklaration (eller kanske man skall säga en definition) av hur en datatyp ser ut? Vi visar definitionen för typen String för att visa detta. type String is array (Positive range <>) of Character; Str : String(1..5); Detta betyder att vi har en datatyp som heter String och den består av ett antal tecken. Hur många tecken strängen skall innehålla krävs att man anger när man deklarerar sin variabel. I typedeklarationen står det "Positive range <>". Detta betyder att man när man senare skall deklarera sin variabel måste ange de gränser som fältet har och gränserna måste dessutom vara av typen "Positive", d.v.s. vara positiva heltal större än noll. Man brukar kalla "<>" för "Ada-box". Vi kan göra motsvarande sak med heltal om vi vill. Vi använder samma exempel som tidigare. type Integer_Array is array (Positive range <>) of Integer; A : Integer_Array(1..18); Observera att man inte får deklarera en variabel att ha en odefinierad storlek i Ada. Det går alltså inte att göra följande deklaration: A : Integer_Array; Ger kompileringsfel! Däremot kan man i ett underprogram deklarera en parameter av en icke fullständigt specificerad datatyp och sen ta emot data som är fullständigt specificerade av motsvarande datatyp. Exempel på detta följer här. with ...; use ...; procedure Xxx is type Integer_Array is array (Positive range <>) of Integer; procedure Yyy(A : in Integer_Array) is begin ... end Yyy; A : Integer_Array(1..18); begin Yyy(A); end Xxx; Detta var lite om variabler som motsvarar vektorer. Nu kan man också representera matriser av godtycklig dimension med hjälp av fält. Antag att vi vill spela luffarschack med tre gånger tre rutor (Tic-Tac-Toe) och att vi vill att datorn skall agera spelbräde. Hur gör vi för att representera själva brädet? X O X X O O Man ser direkt är att det ser ut som en matris. Varför inte lagra detta i ett tvådimensionellt fält? Skulle det gå att lagra i en vektor? Javisst, men det blir mer komplext att hantera för den som programmerar. Vi väljer en 3x3-matris. Vi måste nu bestämma oss för vilket index som motsvarar x- respektive y-led. Inte för att det spelar någon roll för den som spelar utan enbart för att den eller de som programmerar skall veta vad som menas med första respektive andra index. Vi antar att första index är y-led och andra index är x-led (det visar sig vara ganska praktiskt senare). Hur skriver vi deklarationen av ett bräde (hur ser datatypen ut)? type Matrix_Type is array (1..3, 1..3) of _____; Ja, vad skall vi ha för datatyp på respektive ruta? Förslag som ofta brukar dyka upp är Boolean (verkar ju bara vara två olika värden) och Integer (0=tom ruta, 1=den ena spelaren, 2=den andra spelaren), men ibland kommer någon på att det kanske vore "smart" med Character då detta ju exakt motsvarar det som skall visas på skärmen. Ta gärna diskussionen om att man behöver en datatyp som har tillräckligt många värden. Vi väljer Character som datatyp för respektive ruta. type Matrix_Type is array (1..3, 1..3) of Character; M1, M2 : Matrix_Type; Vilka operationer kan man utföra på ett fält av denna typ? begin M1(1, 1) := 'X'; M2 := M1; Put(M2(1, 1)); Put(M2); ERROR!!! Finns fortfarande ingen Put för fält. M2(1) := M1(1); ERROR!!! Går ej att kopiera en rad. p.g.a. att deklarationen anger att det måste vara två index. end ...; Det sista felet kanske vi skulle vilja slippa. Antag att man skulle vilja kopiera en rad från den ena variabeln till den andra. Hur gör man då? Ja, det bästa är att göra en annorlunda deklaration av typen. Så som typdeklarationen ser ut nu måste båda index anges (om man vill ange något index). type Row_Type is array (1..3) of Character; type Matrix_Type is array (1..3) of Row_Type; M1, M2 : Matrix_Type; Nu är det tillåtet att göra samma operationer som tidigare på variablerna, men dessutom går det att behandla en rad separat. En rad har ju en egen typ. Det har dock blivit lite annorlunda hur man skriver indexen. Man måste nu ha ett parentespar till respektive index. begin M1(1)(1) := 'X'; M2 := M1; Put(M2(1)(1)); M2(1) := M1(1); Tillåtet nu! Antag att vi har spelat en stund och vi vill se hur brädet ser ut. Vi antar att brädet representeras av M1 ovan. Vi gör ett anrop till en egendefinierad procedur som vi kan kalla Put och ser hur vi deklarerar denna procedur. procedure Xxx is type Matrix_Type is array (1..3)(1..3) of Character; procedure Put(Item : in Matrix_Type) is begin Hur gör vi??? end Put; M1 : Matrix_Type; begin ... Put(M1); ... end Xxx; Frågan är nu hur man gör för att skriva ut brädet. Det kan man fundera på en stund och är man inte van programmerare kan man kanske tycka att detta är alldeles för komplext. Vi tar det steg för steg och vi börjar med ett specialfall. Hur skriver man ut brädets första ruta? Vi gör en kodsnutt som är ganska klumpig, men som vi sen kan förbättra. procedure Put(Item : in Matrix_Type) is begin Put(Item(1)(1)); Put(Item(1)(2)); Put(Item(1)(3)); New_Line; Put(Item(2)(1)); Put(Item(2)(2)); Put(Item(2)(3)); New_Line; Put(Item(3)(1)); Put(Item(3)(2)); Put(Item(3)(3)); New_Line; end Put; Det verkar som om vi kan göra tre slingor som kan skriva ut de olika raderna. Vi förbättrar koden lite. procedure Put(Item : in Matrix_Type) is begin for X in 1..3 loop Put(Item(1)(X)); end loop; New_Line; for X in 1..3 loop Put(Item(2)(X)); end loop; New_Line; for X in 1..3 loop Put(Item(3)(X)); end loop; New_Line; end Put; Om man tittar lite närmare på koden ser man att slingorna egentligen ser väldigt lika ut. Kanske man kan ha ytterligare en slinga utanför och få bara en uppsättning? Tycker man att detta är konstigt skulle man också kunna säga att man gör ett underprogram för att skriva ut en rad givet att man skicka med vilken rad det är samt själva brädet. Vi modifierar koden. procedure Put(Item : in Matrix_Type) is begin Skriv ut hela brädet. for Y in 1..3 loop Skriv ut en rad. for X in 1..3 loop Skriv ut en position på en rad. Put(Item(Y)(X)); end loop; Ny rad så att inte allt kommer på en lång rad. New_Line; end loop; end Put; Samma igen, men med underprogram (gör att man inte blandar ihop vilken slinga som gör vad). procedure Put(Item : in Row_Type) is begin Skriv ut en rad. for X in 1..3 loop Skriv ut en position på en rad. Put(Item(X)); end loop; Ny rad så att inte allt kommer på en lång rad. New_Line; end Put; procedure Put(Item : in Matrix_Type) is begin Skriv ut hela brädet. for Y in 1..3 loop Put(Item(Y)); end loop; end Put; Observera att man inte kan skriva "M1(1, 1)" om man deklarerat typen på det andra sättet och det inte heller går att skriva "M1(1)(1)" om man deklarerat enligt den första modellen. För att klara av laborationen behöver vi också ta upp lite om hur man gör för att sortera data i ett fält. Det finns massor av algoritmer som är olika effektiva på olika datamängder, men vi tar en enkel modell bara för att ha något att börja med. Det är tillåtet att använda olika algoritmer i laborationen. Algoritmen för "Bubble Sort" (antag ett fält med 20 positioner fyllt med heltal): 1. Bubbla upp det minsta värdet till position 1 (toppen). Att bubbla upp ett värde går till så att man börjar längst ner (index 20) och jämför intilliggande index. Om det data som ligger på det högre indexet är mindre än det ovanför skall man byta plats på dessa. Fortsätt hela vägen upp. 2. Bubbla upp det näst minsta värdet till position 2. 3. O.s.v. För att lösa detta (som antagligen är lite komplext för en nybörjare) bör man börja med att lösa ett specialfall. T.ex. kontrollera och (eventuellt) byt plats på data i de två högsta indexen. Detta löser vi på lektion. så att alla får vara med och fundera på lösningen. För att principen skall vara glasklar kan man avslutningsvis ta fram fem personer som skall sorteras i längdordning eller i alfabetsordning på förnamn eller liknande. Det skall dock vara något som alla i auditoriet lätt kan uppfatta (fungerar inte bra med alfabetsordning i stora grupper alltså). Om man får tid över kan man alltid ta upp att det i standarden för Ada står att det går att jämföra två fält (av samma längd) utan att behöva gå igenom alla index var för sig. Detta gäller dock endast om varje data i fältet är av enkel datatyp. Det går alltså att jämföra två strängar med varandra och se vilken som innehåller det "minsta" datat på följande sätt: if Str1 < Str2 then ... end if; Effekten blir att jämförelseoperatorn "<" går letar efter första tecknet som inte är lika i de båda strängarna och då avgör vilken sträng som är "minst". T.ex. är "Kalle" < "Nisse" och "AAA" < "AAB". På samma sätt går det alltså att jämföra två fält med t.ex. heltal eller flyttal, men inte två fält där datat i respektive index är av någon komplex typ (t.ex. ett annat fält). Övning på lektion Uppgift 1: Läs in 5 heltal, summera dessa och skriv sedan ut summan på skärmen. Lös uppgiften med underprogram och fält. Låt gruppen hjälpa till att lösa detta problem och vara bara den som styr hela programmeringsarbetet. Låt olika personer hjälpa till och diskutera och ifrågasätt varför man väljer procedurer respektive funktioner. Skriv ett fullständigt program.Var börjar vi och hur gör vi för att slippa så många fel som möjligt? Observera att vi från början inte behöver bry oss om vilka paket vi måste inkludera. Det är först när vi behöver ett paket som vi tar med det. procedure Summera_version_3 is begin Här skall det in mer kod. end Summera_version_3; Vi fyller i med några satser. Antag att vi har tre rader på oss att klara av hela uppgiften. Hur gör vi? procedure Summera_version_3 is begin Las_In(Talen); Summera(Talen, Summan); Skriv_Ut(Summan); end Summera_version_3; Vi måste deklarera variabler, men då måste vi nog göra en typ också. Man kan inte skicka fält som parametrar utan att först deklarera en datatyp för fältet (ses inte som samma datatyp i anrop och parameterdefinition annars). procedure Summera_version_3 is type Arr5 is array (1..5) of Integer; Talen : Arr5; Summan : Integer; begin Las_In(Talen); Summera(Talen, Summan); Skriv_Ut(Summan); end Summera_version_3; Nu kommer vi till definitionerna av underprogrammen. Skall det vara funktioner eller procedurer? Det bör studenterna komma fram till. Vissa kanske tror att "Summera" är en funktion därför att den beräknar ett värde som skickas tillbaka. Detta är en intressant synpunkt och tillfället bör tas till vara. Ge förslaget att vi skriver en procedur nu och senare skriver vi om den till en funktion för att se vad som skiljer dem åt. Vilken procedur skall vi börja med? Vissa kanske tror att man måste programmera i samma ordning som man senare skall utföra programmet, men man kan här ta upp att det kan vara olika programmerare som gör olika delar. Vi kan börja med "Las_In" i vilket fall. Vi markerar vart i programmet underprogrammen skall skrivas. Observera att det är behändigt att lägga underprogrammen ovanför variablerna då detta ger att man måste skicka data via parametrarna och på det viset slipper "konstiga sidoeffekter". Det är dock tillåtet att skriva dem nedanför variablerna om man bara gör på "rätt sätt" med parametrarna. Låt någon student hjälpa till med procedurhuvudet. Vad skall proceduren heta och hur många parametrar skall det vara och vad skall dessa heta samt skall dessa vara "in", "out" eller "in out"? Någon kanske tror att det skall vara "in" därför att vi läser in data från tangentbordet. Rita figur som visar att vi bara tittar på gränssnittet gentemot den som anropar oss (i detta fall huvudprogrammet). procedure Las_In(Talen : out Arr5) is begin Här skall det in mer kod. end Las_In; Går programmet att kompilera? Nej, vi saknar två underprogram och dessutom måste det stå minst en sats mellan "begin" och "end". Vi skulle kunna skriva satsen "null;" och sen göra de två andra procedurerna också för att kunna kompilera och se att vi är på rätt väg. Kan vara bra när man sitter själv och programmerar på laborationerna. Vi fortsätter utan detta på lektionen och låter studenterna fylla i satserna i proceduren. procedure Las_In(Talen : out Arr5) is begin for I in 1..5 loop Get(Talen(I)); end loop; end Las_In; Frågor som kan ställas är om I skall deklareras och det är något paket vi behöver till vårt program (Ada.Integer_Text_IO behövs för att kunna göra Get på ett heltal). Paketen skall inkluderas ovanför huvudprogrammet. Vissa undrar om man måste ha samma namn på parametrarna som variabeln i huvudprogrammet. Svaret är nej, endast ordning, antal och datatyp är relevant. Det kan vara studenter som tycker att man kan låta bli att skicka parametrar. Det fungerar ju ändå. Kan motiveras genom att man normalt sett skriver stora (jättestora) system som gör att man inte kan ha alla variabler i huvudprogrammet och dessutom är det så att genom att skicka data via parametrar är det möjligt att kopiera in kod i ett helt annat program och det fungerar fortfarande. Skulle det inte göra om man inte råkade ha samma variabler i det huvudprogrammet. Vi fortsätter med summeringen. Återigen en student som får hjälpa till med procedurhuvudet och sen gäller det att få fram hur satserna skall se ut. procedure Summera(Talen : in out Arr5; Summan : out Integer) is begin Summan := 0; for I in 1..5 loop Summan := Summan + Talen(I); end loop; end Summera; Det blev inga extra lokala variabler här heller. Vi ger oss på utskriftsproceduren. Samma upplägg igen. Studenterna arbetar. procedure Skriv_Ut(Summan : in Integer) is begin Put("Summan blev: "); Put(Summan, Width => 0); New_Line; end Skriv_Ut; Här kommer "Ada.Text_IO" att behövas. Vad händer om man inte tar med "Width => 0"? Vi markerar återigen var alla procedurer skall skrivas så att inget missförstånd uppstår. Man kan lägga till att "Get" är beroende av att användaren matar in vettiga värden. Annars kanske man skulle ha gjort en separat "säker" inläsningsprocedur. Nu ger vi oss på att göra om proceduren som summerar till en funktion. Vad kommer att ändras (fråga studenterna). Vi börjar i huvudprogrammet. Anropet till funktionen måste se ut på följande sätt: Summan := Summera(Talen); Vi skickar in hela fältet med tal och funktionen beräknar ett värde (summan) som returneras och tilldelas till variabeln "Summan". Observera att funktioner måste anropas i "uttryck" och att de inte får anropas som en egen sats. Proceduren måste också modifieras. Vi skriver om hela från början för att låta studenterna jobba lite med parametrar m.m. igen. Funktionshuvudet, parametrar och "in", "out" ...? function Summera(Talen : in Arr5) return Integer is begin Summan := 0; for I in 1..5 loop Summan := Summan + Talen(I); end loop; end Summera; Mellan "begin" och "end" har vi nu fått in samma kod som i proceduren sen tidigare. Är det något so minte stämmer (eller kanske saknas)? Ja, man måste returnerna resultatet till den som anropar. Hur gör man detta? function Summera(Talen : in Arr5) return Integer is begin Summan := 0; for I in 1..5 loop Summan := Summan + Talen(I); end loop; return Summan; end Summera; Är allt ok nu? Nej, vi saknar en lokal variabel i funktionen. Nu har vi inte suman som parameter utan måste hantera den med en extra variabel i funktionen. Observera att den lokala variabeln är en annan variabel än den i huvudprogrammet trots att de har samma namn. Vi rättar till detta och efter en kort diskussion kan man nog räkna med att man kan tilldela "Summan" värdet 0 redan i deklarationen. function Summera(Talen : in Arr5) return Integer is Summan : Integer := 0; begin for I in 1..5 loop Summan := Summan + Talen(I); end loop; return Summan; end Summera; För att klara av laborationen behöver vi nog göra ett exempel som visar hur man sorterar data i ett fält. Principen för "Bubble Sort" är genomgången på föreläsning och studenterna bör kunna tala om hur man skall göra (även om de kanske inte kan lösa det med programkod). Rita en figur som visar ett fält med 5 heltal (samma som ovan kanske) där index 1 är överst. Vi börjar med ett specialfall och förbättrar koden efter hand. Antag att vi bara skall sortera värdena i index 4 och index 5 i vårt fält. Hur gör vi? if Talen(5) < Talen(4) then Swap(Talen(5), Talen(4)); end if; Vi upprepar detta för index 3 och 4, index 2 och 3 och sista gången index 1 och 2 och då kanske vi ser att koden blir väldigt lika för alla dessa fall. if Talen(4) < Talen(3) then Swap(Talen(4), Talen(3)); end if; if Talen(3) < Talen(2) then Swap(Talen(3), Talen(2)); end if; if Talen(2) < Talen(1) then Swap(Talen(2), Talen(1)); end if; Vi kanske skulle kunna ersätta talen 5, 4, 3, 2 med en variabel och låta variabeln få värdena i rätt ordning samt göra en slinga som upprepar koden 4 gånger. Vilken typ av slinga? Man bör komma fram till att "for"-satsen (t.ex. med styrvariabel "J") är den lämpligaste samt att man måste gå igenom den baklänges. Man bör dessutom från studenterna kunna få fram att "J - 1" motsvarar det andra indexet man testar. for J in reverse 2..5 loop if Talen(J) < Talen(J 1) then Swap(Talen(J), Talen(J 1)); end if; end loop; Det vi gjort nu motsvarar en "bubbling" och vi vet nu att det första talet ligger på rätt plats. Vi får göra ytterligare tre bubblingar för att få de resterande på rätt plats. Vi skriver koden igen (minst en gång för att se sambandet). for J in reverse 3..5 loop if Talen(J) < Talen(J 1) then Swap(Talen(J), Talen(J 1)); end if; end loop; Det räcker att gå från 5 till 3 denna gång (det översta talet var ju redan klart). Sen räcker det med 5 till 4 och sista gången 5 till 5 (annars kan de två sista talen ligga i fel). Borde man inte kunna göra ytterligare en slinga utanför den andra där styrvariabeln (t.ex. I) får ersätta startvärdet i den inre slingan? for I in 2..5 loop for J in reverse I..5 loop if Talen(J) < Talen(J 1) then Swap(Talen(J), Talen(J 1)); end if; end loop; end loop; Ja, då har vi faktiskt löst hela problemet. Hur gjorde vi? Jo, vi tog ett specialfall och skrev en klumpig lösning och sen förbättrade vi den allt eftersom. Man skall eftersträva att göra lösningar som på något sätt bygger på en algoritm och inte är en fullständig uppräkning av olika fall. Annars har man ett stycke kod som bara fungerar för detta fall och inte enkelt går att modifiera till ett mer generellt problem. Koden ovan skulle kunna göras bättre genom att ersätta de två talen 2 och 5 med konstantnamn som enkelt kan modifieras i programmet. Dessa motsvarar dessutom i princip start- och slutindex i fältet. Kanske man skulle fundera på. I Ada finns det möjlighet att ta fram start och slutindex samt några andra saker för ett fält. Antag följande deklaration av ett fält: type Arr is array (3..7) of Integer; A : Arr; Här följer en kort lista på "bra-att-ha-saker". De kallas "attribut" och gäller i detta fall för alla typer av fält. * Första index i fältet: A'First =3 * Sista index i fältet: A'Last =7 * Fältets indexintervall: A'Range = 3..7 * Längden på fältet: A'Length =5 Man kan använda sig av dessa t.ex. för att göra slingor som går över generella fält. Exempel: for I in A'Range loop Put(A(I)); end loop; for I in A'First..A'Last loop Put(A(I)); end loop; Observera att följande slinga inte alltid är så lyckad. for I in 1..A'Length loop Put(A(I)); end loop; Om föreläsaren inte hunnit gå igenom hur "<" fungerar på fält bör detta gås igenom på lektionen. Se anteckningarna i slutet av föreläsningsdelen om detta isåfall.
© Copyright 2024