Kapitel 7. Array. och andra datastrukturer

Relevanta dokument
Klassdeklaration. Metoddeklaration. Parameteröverföring

Lite om felhantering och Exceptions Mer om variabler och parametrar Fält (eng array) och klassen ArrayList.

Innehållsförteckning

Datatyper och kontrollstrukturer. Skansholm: Kapitel 2) De åtta primitiva typerna. Typ Innehåll Defaultvärde Storlek

Programmera i C Varför programmera i C när det finns språk som Simula och Pascal??

Objektorienterad programmering Föreläsning 9. Copyright Mahmud Al Hakim Agenda (halvdag)

TDIU01 - Programmering i C++, grundkurs

Att förstå hur man konstruerar modulära program Att kunna skapa nya funktioner Att förstå hur data skickas mellan funktioner

Idag. Javas datatyper, arrayer, referenssemantik. Arv, polymorfi, typregler, typkonvertering. Tänker inte säga nåt om det som är likadant som i C.

Grundläggande programmering, STS 1, VT Sven Sandberg. Föreläsning 12

Kompilering och exekvering. Föreläsning 1 Objektorienterad programmering DD1332. En kompilerbar och körbar java-kod. Kompilering och exekvering

Programmeringsteknik och Matlab. Dagens program. Viktiga datum. Repetitionsexempel. Repetition av if/else, for, while och Scanner

Föreläsning 2, vecka 8: Repetition

6.5 Spelserien Gissa tal

[] Arrayer = Indexerad variabel

(Man brukar säga att) Java är... Denna föreläsning. Kompilering av Java. Historik: Java. enkelt. baserat på C/C++ Allmänt om Java

Objektorienterad programmering i Java

Föreläsning REPETITION & EXTENTA

Föreläsning 6: Metoder och fält (arrays)

Parameteröverföring. Exempel. Exempel. Metodkropp

String [] argv. Dagens Agenda. Mer om arrayer. Mer om arrayer forts. String [] argv. argv är variabelnamnet. Arrayer och Strängar fortsättning

Typkonvertering. Java versus C

Dagens föreläsning. Repetition. Repetition - Programmering i C. Repetition - Vad C består av. Repetition Ett första C-program

EDAA20 Programmering och databaser. Mål komprimerat se kursplanen för detaljer. Checklista. Föreläsning 1-2 Innehåll. Programmering.

725G61 - Laboration 2 Loopar och arrayer. Johan Falkenjack

Agenda. Arrayer deklaration, åtkomst Makron Flerdimensionella arrayer Initiering Strängar Funktioner och arrayer. Övningar nu och då

Programmering i C++ En manual för kursen Datavetenskaplig introduktionskurs 5p

Lektion 7. Datateknik A, Java I, 5 poäng

4 Sammansatta datatyper

Tentamen DE12, IMIT12, SYST12, ITEK11 (även öppen för övriga)

Föreläsning 3-4 Innehåll. Diskutera. Metod. Programexempel med metod

Programmering med Java. Grunderna. Programspråket Java. Programmering med Java. Källkodsexempel. Java API-exempel In- och utmatning.

Föreläsning 3-4 Innehåll

Java, klasser, objekt (Skansholm: Kapitel 2)

Anmälningskod: Lägg uppgifterna i ordning. Skriv uppgiftsnummer (gäller B-delen) och din kod överst i högra hörnet på alla papper

TDDC77 Objektorienterad Programmering

Arrayer (vektorer) Murach s: kap Elektronikcentrum i Svängsta AB

F4. programmeringsteknik och Matlab

Användarhandledning Version 1.2

Visual Basic, en snabbgenomgång

Föreläsning 5-6 Innehåll. Exempel på program med objekt. Exempel: kvadratobjekt. Objekt. Skapa och använda objekt Skriva egna klasser

TDIU01 - Programmering i C++, grundkurs

Datastrukturer. Erik Forslin. Rum 1445, plan 4 på Nada

Arrayer. results

Uppgiften är att beskriva en kvadrat i ett Java program. En första version av programmet skulle kunna se ut så här:

Pekare. Pekare. Varför använder vi pekare? Vad är en pekare? Pekare. Deklaration/initiering av pekare

Föreläsning 5-6 Innehåll

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Muddy. Funktioner / metoder. Punktnotation. Evalueringsordning

Ett problem. Kontrollstrukturer och arrayer. Arrayer. Lösningen. Arrayer och hakparanteser. Exempel int[] results; results = new int[10]; // 0..

Att använda pekare i. C-kod

2D1311 Programmeringsteknik för Bio1 och Bio2, vt 2003 Fiktivt prov På flervalsfrågorna är endast ett svar rätt om inget annat anges i frågan! Det rik

Arrayer (fält)

Planering Programmering grundkurs HI1024 HT 2014

Exempel. Arrayer. Lösningen. Ett problem. Arrayer och hakparanteser. Arrayer

Programmering B med Visual C

Datalogi I, grundkurs med Java 10p, 2D4112, Fiktiv tentamen, svar och lösningar och extra kommentarer till vissa uppgifter 1a) Dividera förs

Introduktion till arv

C++ Slumptalsfunktioner + switch-satsen

Att deklarera och att använda variabler. Föreläsning 10. Synlighetsregler (2) Synlighetsregler (1)

Inledande programmering med C# (1DV402) 27+15=42 1 (22)

Lösningsförslag: Instuderingsfrågor, del D

1 Texthantering. 1.1 Typen char. Exempel, skriv ut alfabetet

Föreläsning 2 Objektorienterad programmering DD1332. Typomvandling

Frekvenstabell över tärningskast med C#

Föreläsning 2. Täcker material från lektion 1, 2, 3 och 4:

4.4 Swing ett interaktivt grafiskt gränssnitt

C++ - En introduktion

Föreläsning 11. Arrayer. Arrayer. Arrayer. Lagrar flera värden av samma typ Kan vara primitiva typer eller objekt. Kan ha en array av t.

Planering Programmering grundkurs HI1024 HT data

Föreläsning 5: Introduktion av pekare

Teoretisk del. Facit Tentamen TDDC (6)

Kapitel 6. Hakparenteser fšr att ange index MŒnga všrden av samma typ

Digitalitet. Kontinuerlig. Direkt proportionerlig mot källan. Ex. sprittermometer. Elektrisk signal som representerar ljud.

SMD 134 Objektorienterad programmering

Kungl. Tekn. Högskolan Förel 1, bild 1 Föreläsning 1: Introduktion ffl Kursinnehåll ffl Javarepetition ffl Referenser ffl Nyckelordet static ffl Klass

Metodanrop - primitiva typer. Föreläsning 4. Metodanrop - referenstyper. Metodanrop - primitiva typer

Föreläsning 8 SLUMPTAL, SIMULERING + INTRODUKTION TILL VEKTORER

Funktionens deklaration

Grundkurs i programmering, 6 hp (725G61) Dugga 2 tillfälle 2

Högskolan Dalarna sid 1 av 7 DI-institutionen Hans-Edy Mårtensson Sten Sundin

Programmeringsteknik med C och Matlab

Övning från förra gången: readword

Laboration A Objektsamlingar

MMA132: Laboration 2 Matriser i MATLAB

Grundläggande programmering med C# 7,5 högskolepoäng

Planering Programmering grundkurs HI1024 HT TIDAA

Objekt och klasser - Introduktion

EDAA20 Programmering och databaser. Mål komprimerat se kursplanen för detaljer. Om att lära sig programmera. Föreläsning 1-2 Innehåll.

2 Pekare och dynamiska variabler.

Programmering, grundkurs, 8.0 hp HI1024, HI1900 etc., Tentamen TEN1. Måndagen den 10 januari 2011,

Minnestilldelning (allokering) och frigörande (avallokering) av minne

Övningsuppgifter kapitel 8

Inlämningsuppgift : Finn. 2D1418 Språkteknologi. Christoffer Sabel E-post: csabel@kth.se 1

TDIU01 - Programmering i C++, grundkurs

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Felsökning. Datatyper. Referenstyper. Metoder / funktioner

Bankkonto - övning. Övning 2 Skriv en metod, geträntan, som returnerar räntan.

Beräkningsvetenskap föreläsning 2

DIAGNOSTISKT PROV. Tid. Hjälpmedel. Antaganden. Rättning. Övrigt. Diagnostiskt Prov. Klockan Inga

TDDI14 Objektorienterad programmering

Alla datorprogram har en sak gemensam; alla processerar indata för att producera något slags resultat, utdata.

Transkript:

Kapitel 7 Array och andra datastrukturer Ämne Sida Program 7.1 Vad är en array? 180 7.2 Definition och initiering av en array 183 Array Initieringslista 186 ArrayInit 7.3 foreach-satsen 189 Foreach 7.4 Texthantering med array av char 191 ArrayChar Slumplösenord 192 7.5 Array av referenser 194 ArrayOfRef 7.6 Array som parameter i metoder 197 Arrayparam 7.7 Sökning och sortering 201 RandArray - Slumptal i en array 201 Search - Bubbelsortering 204 Bubble 7.8 Kryptering av text 208 EncryptChar 7.9 Dynamiska arrays: Listor 211 List Övningar till kapitel 7 (Projekt Master Mind) 215 179

7.1 Vad är en array? När vi pratade om loopar sade vi så här (sid 95): Datorn har några egenskaper som är helt överlägsna motsvarande egenskaper hos människan: snabbheten, noggrannheten och förmågan att effektivt lagra och hantera stora datamängder samt förmågan att inte bli trött. Kontrollstrukturen repetition i sina olika varianter: while-, for- och do-satser utnyttjar både snabbheten och förmågan att inte bli trött, vilket nu ska kompletteras av ett verktyg som även utnyttjar den andra överlägsna egenskapen: Att kunna effektivt lagra och hantera stora datamängder. Detta verktyg är datatypen array som i engelskan betyder ordnad skara eller ordnad uppställning (battle array = stridsordning). Redan ordets betydelse i det vanliga språket är upplysande och leder oss till den datatekniska termen. Andra beteckningar som man hittar i litteraturen är fält, vektor, matris, lista,. Kärt barn har många namn. Vi kommer dock att använda endast begreppet array. En array är en ordnad mängd av variabler av samma datatyp grupperade under samma namn och lagrade i ett sammanhängande minnesområde. En array består av ett antal numrerade element. Varje elementets position i arrayen kallas index. Index är ett nummer som specificerar varje elements position i arrayen och används för att komma åt ett element. Elementen kan vara av enkel, sammansatt eller klasstyp. Så man kan gruppera även objekt till en array. Kända datatyper för oss har hittills varit de enkla datatyperna (sid 62) samt objekt av olika klasser. En array är inte längre en enkel utan en sammansatt datatyp, den enklast tänkbara sammansatta datatypen. En sammansatt datatyp representerar fler än ett värde åt gången, t.ex. flera heltal, flera flyttal, flera tecken, flera sanningsvärden. Det finns i litteraturen även det synonyma begreppet härledd datatyp som syftar åt att den är baserad på en annan datatyp. Till att börja med, anta att det är enkla datatyper som ska grupperas till en array. Som exempel tar vi en array som är sammansatt av den enkla datatypen int. Varje element i en sådan array kan betraktas som en indexerad dvs numrerad variabel av typ int. Anta att vi vill definiera 20 heltalsvariabler: Hittills: enkel datatyp int: Nu: int-array med referens: int no1; int no2;. int[] no = new int[20];.. int no20; 180

Hittills har vi skrivit 20 satser (koden till vänster) för att definiera 20 int-variabler. Men nu ger array oss möjligheten att göra samma sak med endast 1 sats (koden till höger): Vi definierar en variabel no, använder new och lägger till informationen om antalet element i slutet. Men av vilken typ är variabeln no? Tittar man närmare på den nya koden finns det likheter med definition av objekt: new allokerar 20 minnesceller för lagring av int-värden och returnerar minneskedjans adress närmare bestämt adressen till dess första cell till referensvariabeln no. Därmed är frågan om den nya variabelns datatyp besvarad: Vi har att göra med en ny referenstyp: Datatypen int[] är en referens till en int-array. För att göra det ännu tydligare kan man skriva den nya koden även i två separata satser: int[] no; no = new int[20]; Det är inte den första utan den andra satsen som skapar själva arrayen. Därför står också 20 arrayens storlek där det behövs, nämligen i satsen där new allokerar minne. Typiskt för array är hakparenteserna [ ], på engelska brackets. I satserna ovan har de två olika betydelser: I den första specificerar int[] variabeln no:s datatyp som en referens till en int-array, i den andra innehåller [20] arrayens storlek. Referensvariabeln no ersätter de 20 vanliga int-variablerna no1, no2,, no20, vilket medför en stor effektivitet i C#-koden. Tänk dig att det är inte 20 utan fler data vi vill jobba med. no pekar på det första elementet av arrayen där varje element lagrar ett annat heltalsvärde. Men hur kommer man åt de andra elementen? Svaret är indexering som är bara ett annat namn för numrering. Indexering i en array Låt oss anknyta till exemplet ovan där både arrayen och dess referens no definieras: int[] no = new int[20]; Låt oss ytterligare anta att vissa värden de som visas i bilden nedan har tilldelats arrayens element efter satsen ovan. Eftersom elementen lagras i ett sammanhängande minnesområde uppstår följande minnesbild av arrayen i datorns RAM: Index: 0 1 2 17 18 19 190d11 25 1257-10... 358 65 219 no[0] no[1] no[2]... no[17] no[18] no[19] no 190d11 Medan själva arrayens allokering (övre delen) görs av new int[20], allokeras minnescellen no (den undre delen) av int[] no. Kopplingen mellan dem görs av tilldelningsoperatorn, vilket gör att arrayens adress (t.ex. 190d11) som new har genererat, hamnar i minnescellen no. Den så uppkomna situationen innebär att no pekar på eller refererar 181

till arrayen, vilket åskådliggörs med pilen. Under arrayens minnesceller står C#-kod som kommer åt varje elements värde: no[0] ger den första minnescellens värde 25 som har index 0, no[1] ger den andra minnescellens värde 1257 som har index 1 osv. no[0] lagras vid adressen till arrayens första minnescell. no[1] lagras vid adressen till den andra minnescellen som ligger 1 x 4 bytes storleken för en int längre bort från no. no[2] lagras vid adressen som ligger 2 x 4 bytes längre bort från no osv. Adressering i RAM sker nämligen byte-vis, så att bytes som är grannar till varandra, har adresser som skiljer sig på en enhet. Avgörande för denna indexeringsteknik är att en array alltid allokeras i ett sammanhängande minnesområde. Ser man på det hela ur adresseringsteknikens synpunkt kan man förstå varför indexnumreringen börjar med 0 och inte med 1: no[0] kan tolkas som den adress som ligger 0 x 4 bytes längre bort från no, dvs no[0]:s adress är identisk med adressen no. Därför gäller följande indexregel: Indexregeln: I en array börjar numreringen av index alltid med 0. Därför gäller: elementets position = index + 1 Med position menas numret som människan använder för att numrera elementen. Människor är vana vid att påbörja numreringen av saker och ting med 1. Med index menas numret som datorn använder för samma sak. C# och de flesta andra programmeringsspråken börjar numreringen av index i en array med 0. Tillämpad på exemplet: Det 1:a elementet i den array som no refererar till har värdet 25 och index 0: Positionen är 1 medan indexet är 0. I C# kodar man det med no[0]. Det 2:a elementet (värdet 1257) har index 1 och koden no[1], det 3:e elementet (värdet 10) har index 2 och koden no[2] osv. Det n:e elementet har alltid index n-1. Därför har också det 20:e elementet (värdet 219) index 19. Det är avgörande när man arbetar med array och samtidigt felkälla nr 1 om man glömmer det att hålla isär det mänskliga sättet att numrera som börjar med 1 från C#- kodens sätt som börjar med 0. I exemplet ovan har vi definierat en array av 20 heltalselement med referenserna no[0],..., no[19]. Antalet element är 20. Indexen däremot går från 0 till 19. Felkälla nr 2 är att förväxla en arrayelements index med dess värde: Det sista elementet i exemplet ovan har index 19, men värdet 219. Man har alltid med två tal att göra, index (position) och värde (innehåll). Det gäller att hålla isär positionen från innehållet. Tre egenskaper skiljer objekt från array: Indexering Allokering i ett sammanhängande minnesområde Alla arrayelement har samma datatyp. Annars behandlas array i C# som objekt: Båda måste skapas med new och man kan komma åt båda endast med referensvariabler. Båda initieras till default-värden även om de förekommer som lokala variabler i metoder (sid 131), vilket vi ser i nästa avsnitt. 182

7.2 Definition och initiering av en array Följande program testar allt vi sagt i förra avsnitt om array speciellt indexregeln. Utöver det visas ytterligare en egenskap hos array som relaterar den till objekt, nämligen en egenskap Length som lagrar arrayens storlek när den skapas. Programmet demonstrerar också vad som händer om man överskrider arrayens maximala index: Man kan kompilera, men inte exekvera ett tecken på att arrayens allokering sker vid run time. // Array.cs // Definierar en array av 4 int-värden, visar default-initie- // ringsvärdena 0, tilldelar och skriver ut de nya värden // Skriver ut arrayens storlek med Array-egenskapen Length // Överskridning av arrayens index leder till exekveringsfel using System; class Array static void Main() int[] no; // Deklarerar en referens // utan att skapa arrayen no = new int[4]; // new skapar arrayen vars // adress tilldelas referens // int[] no = new int[4]; // Alternativt i EN sats Console.WriteLine("Default-initiering:"); for (int i=0; i < no.length; i++) Console.WriteLine("Arrayens " + (i+1) + ":a element" + " med index " + i + " har värdet " + no[i]); no[0] = 64; // Tilldelar 1:a elementet no[1] = 86; // värdet 64 osv. Överskriver no[2] = 34; // default-initieringen no[3] = -6; Console.WriteLine("\nEfter tilldelning:"); for (int i=0; i < no.length; i++) Console.WriteLine("Arrayens " + (i+1) + ":a element" + " med index " + i + " har värdet " + no[i]); Console.WriteLine( "\n\töverskridning av arrayens index leder till " + "exekveringsfel!\n\n\tex.: no[4] inte definierad\n\t" + " Index 4 överskrider gränsen: Programavbrott!") ; no[4] = 1; // no[4] kan kompileras, men // leder till exekveringsfel Inte alla satser i programmet Array exekveras. Det blir avbrott när den kompilerade koden no[4] i allra sista satsen ska exekveras där index 4 överstiger arrayens tillåtna maximala indexgräns som är 3 därför att new i början av programmet allokerar endast 4 minnesceller åt arrayen, nämligen de med index 0, 1, 2 och 3. Någon minnescell med index 4 är inte allokerad. Därför kan vi inte heller referera till den med no[4]. Men ef- 183

tersom arrayens allokering sker med new och därmed under exekveringstid (eng. run time) leder detta till exekveringsfel, medan kompilatorn godtar den syntaxmässigt korrekta koden no[4]. Programmet Array ger följande utskrift när man kör det: Default-initiering: Arrayens 1:a element med index 0 har värdet 0 Arrayens 2:a element med index 1 har värdet 0 Arrayens 3:a element med index 2 har värdet 0 Arrayens 4:a element med index 3 har värdet 0 Efter tilldelning: Arrayens 1:a element med index 0 har värdet 64 Arrayens 2:a element med index 1 har värdet 86 Arrayens 3:a element med index 2 har värdet 34 Arrayens 4:a element med index 3 har värdet -6 Överskridning av arrayens index leder till exekveringsfel! Ex.: no[4] inte definierad Index 4 överskrider gränsen: Programavbrott! Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array. at Array.Main(String[] a) in C:\C#\MyProject\54Array.cs:line 33 Vi drar slutsatsen: Att referera till icke-definierade element i en array leder till exekveringsfel. Man kan även säga att C#-interpretatorn (VM) kontrollerar indexgränserna och inte tillåter åtkomsten till icke-allokerade minnesplatser, vilket ur allmän datasäkerhetssynpunkt är en fördel. Programmen blir stabilare. Andra programmeringsspråk som C++ har i detta avseende en mer liberal attityd. Där ligger ansvaret för kontroll av indexgränserna helt och hållet hos programmeraren. Man kan ju undra varför no[4] inte är definierat som vi hävdar ovan fast talet 4 förekommer i definitionssatsen new int[4]. Detta beror på att hakparenteserna [] i no[4] inte har samma betydelse som i new int[4]. Den korrekta tolkningen av [] beror på sammanhanget. Man kan också säga att [] är symbolen för tre olika operatorer som överlagrar varandra dvs betyder olika i olika sammanhang (sid 16/170): Hakparentesernas tre olika betydelser 1. [] som storleksoperator omsluter i definitioner med new antalet element i arrayen specificerar därmed arrayens storlek. T.ex. innebär koden new int[4] 184

i programmet Array att new skapar en array av int med 4 element dvs att 4 minnesceller reserveras för lagring av int-värden. Det gemensamma för alla dessa element är att de lagras en efter den andra vid adressen eller referensen no: no 0 0 0 0 Här är frågan om Hur många element?. I matematiken kallas detta kardinaltal. 2. [] som indexeringsoperator omslutar indexet till varje element av en array. Här handlar det om ett elements position i arrayen. Man anger index inom hakparenteser för att referera till elementet när man vill hämta eller tilldela det ett värde. Indexregeln (sid 182) tillämpas enligt vilken indexeringen börjar med 0. Därför är no[4] i arrayen ovan inte definierat: no no[0] no[1] no[2] no[3] Här är frågan om Vilket element?. I matematiken kallas detta ordinaltal. 3. [] som en del av datatypen referens till array omsluter ingenting utan är tom och skrivs direkt efter en datatyp för att definiera en ny referenstyp. T.ex. innebär satsen int[] no; i programmet Array att en minnescell allokeras (en referensvariabel med namnet no definieras) för lagring av en adress till en int-array. Vi kan i fortsättningen använda namnet no för att komma åt arrayen vid denna adress. I satsen ovan har referensen no inte initierats. Det sker inte heller automatiskt, för no är en lokal variabel i Main(). Det sker först med tilldelningen no = new int[4]; som initierar rederensen explicit. Default-initiering av en array Det anmärkningsvärda är nu att det som gäller för referensen no att den är oinitierad när den skapas inte gäller för själva arrayen. Referensen no är oinitierad och måste initieras explicit eftersom den är en lokal variabel i Main(). Men trots att även arrayen är lokal i Main() initieras den till de default-värden vi nämnde för datamedlemmar i objekt (sid 131), vilket är ett tecken på att array även i detta avseende behandlas som objekt. Programmet Array skriver ut arrayelementens värden en gång innan och en andra gång efter att de har fått värdena 64, 86, 34 och -6. Utskriften på förra sidan visar för arrayens alla element initialvärdet 0 som är den föreskrivna default-initieringen för variabler av typ int vilket även gäller för element i en int-array. Generellt gäller: Alla element i en array initieras automatiskt till default-värden (precis som datamedlemmar i ett objekt) även om arrayen skapas lokalt i en metod. 185

Initieringslista Precis som det finns skillnader i definitionen av arrayvariabler (referenser) jämfört med vanliga variabler, finns även skillnader vid tilldelningen. T.ex. är tilldelningen av arrayen no i programexemplet Array (sid 183) en sats för varje element inte särskilt effektiv, speciellt om man skulle tillämpa samma teknik på större arrayer med 100-tals eller fler element. Men just hanteringen av stora datamängder var ju motiveringen för att syssla med array. Man kan effektivisera hanteringen genom att använda sig av forsatser och av en s.k. initieringslista, en kortform som slår ihop definitionen med initieringen. Exempel på båda visas i följande program: // ArrayInit.cs // Kortform för definition och initiering av en array i EN och // samma sats med hjälp av en initieringslista // Elementvis utskrift av en array kan med fördel göras med en // for-sats: Arrayens index = for-satsens räknare using System; class ArrayInit static void Main() int[] no = 64, 86, 34, -6 ; // Initieringslista: // Definition OCH ini- // tiering av en array // int[] no = new int[4] 64, 86, 34, -6 ; // Gör samma // sak Console.Write("\nVärdena från arrayen skrivs ut med" + " referensen:\n\n\t"); for (int i = 0; i < no.length; i++) Console.Write(no[i] + "\t"); int[] copy = no; // Ny referens till // samma arary Console.Write("\n\nVärdena från arrayen skrivs ut" + " med den nya referensen copy:\n\n\t"); for (int i = 0; i < copy.length; i++) Console.Write(copy[i] + "\t"); Console.WriteLine('\n'); En körning visar att värdena i initieringslistan som först tillelas arrayen no verkligen kopierats över till arrayen copy, för det är de som skrivs ut: Värdena från arrayen skrivs ut med referensen no: 64 86 34-6 Värdena från arrayen skrivs ut med den nya referensen copy: 64 86 34-6 186

Både definitionssatsen och initieringssatserna i programet Array (sid 183) det är de 5 första satserna i Main() kan slås ihop till en enda sats: Satsen ovan är bara en förkortning på: int[] no = 64, 86, 34, -6 ; int[] no = new int[4] 64, 86, 34, -6 ; Dvs initieringslistan kan skrivas efter new int[4] som egentligen skapar eller definierar arrayen. Men new int[4] får utelämnas. Detta visar att den förkortade versionen gör två saker: Först, fram till tilldelningstecknet definieras referensen no (utan någon uppgift om arrayens storlek). Sedan, från och med tilldelningstecknet tilldelas arrayen no:s element fyra värden som står i en kommaseparerad lista grupperad inom klamrarna som kallas arrayens initieringslista. Kortformen gör precis samma sak som satsen med new. Kompilatorn får informationen om arrayens storlek genom att i initieringslistan räkna antalet element inom klamrarna. Det är inte ens tillåtet att explicit ange det korrekta antalet element inom hakparenteserna [ ]. Det blir kompileringsfel om man gör det, därför att no endast är en referens till en array, inte arrayen själv. Observera även att man inte får använda initieringslistan separat utan endast i samma sats som definitionen. Valet av variabelnamnet copy kan vara missledande i följande sats av programmet ArrayInit om man inte beaktar skillnaden mellan referens och array: int[] copy = no; copy blir nämligen en kopia av referensen no i satsen ovan, inte av arrayen en ny referens som kommer att peka på samma array som den gamla referensen no pekar på. Det skapas ingen ny array eftersom det varken finns någon new eller någon initieringslista som skulle ersätta new. Anledningen till detta är som vi konstaterat tidigare följande viktigt faktum: En array i C# är alltid ett objekt som behöver en referens. För att skapa ett objekt måste en new-sats skrivas. En referens definieras utan new. Minnesmässigt lagras arrayen på en och samma adress som från programmet kan nås med referenserna no eller copy: no 64 86 34-6 copy 187

Array i en for-sats Man kan effektivisera array-initieringen genom att använda en loop, med fördel en forsats vars räknare är arrayens index. Exempel på det har vi i ArrayInit: for (int i=0; i < copy.length; i++) Console.Write(copy[i] + "\t"); Här görs en utskrift av ett arrayelement per varv i for-satsen. Utskrifterna skiljs åt med en tabulator. Som avslutningsvillkor för for-satsen har vi valt arrayens längd som i det här fallet är 4, vilket återges av arrayobjektets fördefinierade datamedlem Length. Att for-satsen är den mest använda loopen vid hantering av arrayer är ingen tillfällighet. Det är naturligt att for-satsen lämpar sig bäst för arrayer då det i förväg kända antalet element i en array ger upphov till det i förväg kända antalet repetitioner i for-satsen. Den logiska slutsatsen blir att sätta for-satsens räknare till arrayens index för att gå igenom alla element i arrayen. Därför kan, när en array bearbetas med for-sats, följande teknik användas med fördel: Arrayens index = for-satsens räknare Denna teknik har även använts vid utskrift av arrayen no:s värden i början av programmet ArrayInit. 188

7.3 foreach-satsen Kopplingen mellan array och for-sats som behandlades i slutet av förra avsnitt kan ytterligare förenklas och effektiviseras med en ny kontrollstruktur: foreach-satsen. Den kunde inte tas upp i kapitlet om kontrollstrukturer (kap 6, sid 73) därför att den förutsätter array-begreppet eller liknande sammansatta datatyper, som vi inte hade hunnit gå igenom då. foreach-satsen är idealisk för att kombineras med sammansatta datatyper. Den gör nämligen samma sak som for-satsen, men har en lite annorlunda ja t.o.m. lite enklare syntax än for-satsen, om man är förtrogen med arrays. För att demonstrera denna nya kontrollstruktur skriver vi om de två for-satserna i programmet ArrayInit (sid 186) till foreach-satser: // Foreach.cs // Elementvis behandling av en hel array kan med fördel göras med // en foreach-sats: Arrayens element = foreach-satsens variabel using System; class Foreach static void Main() int[] no = 64, 86, 34, -6 ; Console.Write("\nVärdena från arrayen skrivs ut med" + " referensen no:\n\n\t"); foreach (int element in no) Console.Write(element + "\t"); int[] copy = no; Console.Write("\n\nVärdena från arrayen skrivs ut" + " med den nya referensen copy:\n\n\t") ; foreach (int element in copy) Console.Write(element + "\t"); Console.WriteLine('\n'); I vårt exempel ser huvudet i den första foreach-satsen ut så här: Översatt till svenska: foreach (int element in no) För varje element av arrayen no gör Variabeln element namnet är valt av oss kallas även foreach-satsens iterationsvariabel. Den definieras till int och ersätter for-satsens räknare. Men till skillnad från for-satsens räknare är element inget index (nr) utan en variabel som pekar på själva värdet (innehållet) som står i arrayen. Iteration betyder upprepning. Iterationsvariabeln gör att satsens kropp upprepas, dvs fortskrider från element till element tills alla element är genomgångna. in är ett reserverat ord och betyder av eller element av. no är en re- 189

ferens till arrayen som ska loopas igenom. foreach-satsens enkelhet består i att den till skillnad från for-satsen varken behöver ett start-, steg- eller slutvärde resp. avslutningsvillkor. Den går helt enkelt igenom arrayens alla element, från det första till det sista. Det är själva arrayen som bestämmer start-, steg- och slutvärdena. Variabeln element pekar i varje varv av loopen på resp. arrayelementets värde och kan sedan användas i loopens kropp för att göra det man önskar. I vårt exempel sätts den i följande anrop för att skriva ut arrayens element följt av en tabulator som är till för att få en snygg layout i utskriften: Console.Write(element + "\t"); foreach-satsens iterationsvariabel måste ha samma datatyp som arrayelementen eller en sådan datatyp som arrayelementens datatyp automatiskt kan konverteras till. I vårt exempel har vi int. Det är t.o.m. möjligt att ha egendefinierade datatyper dvs klasser. Ett exempel på det är programmet ArrayOfRef. Där deklareras iterationsvariabeln i en foreach-sats till den egendefinierade klassen Fish (sid 197), för att skriva ut ett Fishobjekts sort, vikt, längd, pris och frakt (sid 195). En viktig egenskap av iterationsvariabeln är att den inte kan ändra arrayelementens värden i foreach-satsens kropp. Den är så att säga read only. I praktiken innebär detta att iterationsvariabeln inte får förekomma till vänster om tilldelningsoperatorn (=) i någon sats i foreach-satsens kropp. Vill man i foreach-satsens kropp ändra på arrayelementens värden måste man använda for-satsen istället med arrayens index som räknare (sid 188). Ett enkelt exempel på detta finns i klassen EncryptChar (sid 209). Där kan for-satsen inte ersättas av foreach-satsen pga iterationsvariabelns read onlyegenskap. Men annars kommer vi i fortsättningen att använda foreach istället för for när det är lämpligt. Programmet Foreach ger samma utskrift som programmet ArrayInit (sid 186). 190

7.4 Texthantering med array av char I början av detta kapitel motiverade vi behandlingen av arrayer med att kunna lagra och bearbeta stora datamängder. Ett exempel på det är texthantering där stora textmängder snarare är regel än undantag. Textbehandling är ett klassiskt område för datortillämpning just pga datorns förmåga att effektivt och snabbt kunna hantera stora datamängder. Vanligtvis kan text framställas med datatypen String som är en klass. Men manipulation (hantering) av ett String-objekt är begränsad till metoder som klassbiblioteket ställer till förfogande. En direkt ändring av en text som skapats med datatypen String är inte möjlig. Har man däremot en array av datatypen char har man obegränsade möjligheter till manipulation i och med man kan gå ned till elementnivå. Följande program demonstrerar ett mycket enkelt fall av texthantering med en array av char: // ArrayChar.cs // Definition och initiering av en array av char // I C# är en array av char inte samma sak som String // char-array tillåter ändring av innehållet, men inte String using System; class ArrayChar static void Main() String str = "Russell"; char[] text = str.tochararray(); // Omvandling av en sträng // till en array av char // char[] tex = "Russell"; // Kompileringsfel: sträng // är INTE array av char Console.WriteLine("\n\tArrayen har längden\t" + text.length); Console.Write("\n\tInnehållet är\t\t"); foreach (char element in text) Console.Write(element); // Elementvis utskrift Console.WriteLine(); // FÖRE ändringen text[0] = 'r'; // Ändring av char-array text[1] = 'i'; Console.Write("\tsom görs om till\t"); foreach (char element in text) Console.Write(element); // EFTER ändringen Console.WriteLine('\n'); Ett körresultat av programmet ovan ger följande utskrift: Arrayen har längden 5 Innehållet är Russell som görs om till rissell 191

Utskriften visar arrayens innehåll före och efter ändringen av två bokstäver i strängen Russell. För att kunna göra denna ändring var det nödvändigt att omvandla Stringobjektet Russell till en array av char med String-metoden ToCharArray(). Resultatet används för att initiera den med char[] text definierade referensen till en chararray. Närmare bestämt är satsen char[] text = str.tochararray(); som gör detta, en kortform för satserna: char[] text; // Referens till en char-array text = new char[5]; // Definition av char-array text = str.tochararray(); // Initiering av char-array Här måste arrayens storlek anges medan den i kortformen inte får anges. Programmet tar själv reda på storleken från strängen Russell. Vi kan sedan få tag i storleken med String-egenskapen Length som enligt utskriften ovan ger 5. En gång omvandlad till char-array kan vi gå in på elementnivå och ändra strängens innehåll med hjälp av arrayens index. Slumplösenord En mer intressant tillämpning av textbehandling med hjälp av char-array visas i följande program som genererar ett slumplösenord: // RandPasswd.cs // Skriver ut ETT slumpvis genererat lösenord med policyn: // 8 tecken = 3 små bokstäver: ASCII-intervall (97, 122) + // 2 siffror (48, 57) + // 3 stora bokstäver (65, 90) using System; class RandPasswd static void Main() Random r = new Random(); char[] password = new char[8]; for (int i=0; i < 3; i++) password[i] = (char) r.next(97, (122+1)); // 3 små // bokstäver for (int i=3; i < 5; i++) password[i] = (char) r.next(48, (57+1)); // 2 siffror for (int i=5; i < 8; i++) password[i] = (char) r.next(65, (90+1)); // 3 stora // bokstäver Console.Write("\n\t"); Console.WriteLine(password); Console.WriteLine(); 192

Ett antal körningar av programmet RandPasswd ger följande slumplösenord: gpf49zlc lrn13vsz ztk27crc rmq53qmy rxg53jim 193

7.5 Array av referenser Hittills har vi bildat arrays endast av de fördefinierade datatyperna int och char. På samma sätt kan man också definiera arrays av alla andra enkla datatyper. Men kan man bilda även arrays av klasser dvs egendefinierade datatyper? Frågan måste preciseras: Menar man arrays av referenser, är svaret ja, därför att klasser referensernas datatyper har exakt samma rättigheter som vilka andra datatyper som helst och kan därför skrivas överallt i koden där en fördefinierad datatyp kan stå. Menar man arrays av objekt, är svaret nej, vilket vi kommer att förklara i detta avsnitt. Vi kommer att inse att en array av objekt inte är nödvändig, när man har en array av referenser vars element pekar på objekt. Referensarrayen gör oss samma tjänst som en objektarray. Av följande klass ska vi sedan i programmet ArrayOfRef (nästa sida) konstruera en array av referenser för att låta dem i sin tur peka på objekt av denna klass: // Fish.cs // Deklarerar klassen Fish med tre datamedlemmar och två metoder using System; class Fish public string sort; public double weight, size; public double Price() return Math.Round(weight * 7.25 / 100); public double Shipping() return Math.Round(weight * 0.02 + size * 0.1); Klassen Fish modellerar en fisk med datamedlemmarna sort, weight och size. En laxforell t.ex. med en viss vikt i gram och en viss längd i cm kan vara ett objekt av denna klass, där laxforell är fiskens sort. Metoden Price() beräknar priset på fisken oberoende av sort, med 7.25 kr per hekto. Metoden Shipping() beräknar transportkostnaden utifrån fiskens vikt och längd genom att t.ex. multiplicera kostnadsfaktorn 0.02 med vikten och 0.1 med längden och addera dem. Båda Metoder returnerar priset och frakten i hela kronor utan ören. Biblioteksmetoden Math.Round() avrundar till närmaste heltal. Självklart kan man anmärka att den här modelleringen har brister ur praktisk synpunkt: Fiskpriser är i praktiken inte oberoende av sorten. Men vi gör medvetet sådana förenklingar i modellen för att förenkla implementeringen och koncentrera oss på det programmeringstekniska konceptet av array av referenser. Vi vill använda detta koncept, för att på ett effektivt sätt skapa många objekt av klassen Fish. För detta ändamål är bristerna i modelleringen irrelevanta. 194

Följande program skapar en array av referenser till Fish-objekt och anropar metoderna Price() och Shipping() för att sedan registrera (skriva ut) alla uppgifter till varje objekt: // ArrayOfRef.cs // Skapar först en array av 5 referenser till Fish-objekt, skapar sedan 5 // enskilda Fish-objekt och tilldelar dem till referenserna i arrayen. using System; class ArrayOfRef static void Main() Fish[] f = new Fish[5]; // Array av referenser // OBS! Inga objekt for (int i = 0; i < f.length; i++) f[i] = new Fish(); // Här skapas objekt och // tilldelas till referens Console.Write("\n\tMata in sorten till fisk " + (i+1) + ":\t"); f[i].sort = Console.ReadLine(); if (f[i].sort.length <= 7) f[i].sort += '\t'; Console.Write("\tMata in vikten till fisk " + (i + 1) + ":\t"); f[i].weight = Convert.ToDouble(Console.ReadLine()); Console.Write("\tMata in längden till fisk " + (i + 1) + ":\t"); f[i].size = Convert.ToDouble(Console.ReadLine()); Console.Write("\nFisksort\tVikt i g\tlängd i cm" + "\tpris\tfrakt\n" + "-------------------------------------------------------------\n"); foreach (Fish element in f) Console.WriteLine(element.sort + "\t " + element.weight + "\t\t " + element.size + "\t\t " + element.price() + "\t " + element.shipping() + '\n') ; I programmet ArrayOfRef skapas en array av 5 referenser till Fish-objekt med satsen: Fish[] f = new Fish[5]; Observera att denna sats inte skapar något objekt alls, för då skulle det behövas koden new Fish() OBS! parentesen som inte finns med i satsen ovan. Förväntar man sig att en array av 5 Fish-objekt skulle skapas med new Fish()[5] så är det fel, för den här koden kan inte kompileras ett tecken på att begreppet array av objekt måste förkastas. Istället måste man gå två steg: Först måste en array av rena referenser definieras som i satsen ovan. Initieringsproblematiken löses automatiskt pga att en array alltid initieras till sin datatyps default-värden och att datatypen referens default-initieras till null (sid 131). Då spelar det ingen roll om det handlar om referenser till objekt av klassen Fish eller av någon annan klass. Sedan kan man fundera hur man explicit initierar referenserna så att de pekar på verkliga objekt av typ Fish. Detta görs i programmet ArrayOfRef med: 195

f[i] = new Fish(); som står i for-satsen. Först efter den här satsen har vi allokerat minnesutrymme för ETT objekt av typ Fish, inte för en array av objekt, för i koden ovan finns inget spår av en sådan array. Detta objekts minnesadress tilldelas referensarray-elementet f[i] där i tack vare for-loopen går från 0 till 4. Vi har endast att göra med en array av referenser till Fish-objekt, för hakparentesen arrayens symbol står efter referensvariabeln f som pekar på denna referensarray. Varje element i denna referensarray pekar i sin tur på ett separat Fish-objekt. De två stegen som tas är: Först från f till referensarrayen och sedan från den till objekten. Det första steget står utanför och det andra steget i forloopen. Efter objektens definition initieras varje objekts datamedlemmar sort, weight och size i for-loopen till värden som läses in. Sedan skrivs ut de fullständiga uppgifterna till varje objekt, dvs även priset samt fraktkostnaden. Anropet av metoderna Price() och Shipping() är inbakade i utskriftssatsen. En körning av programmet ArrayOf- Ref ger följande slutlig utskrift (inmatningarna av sort, vikt och längd för varje fisk är inte avbildade): Array av objekt? För att kunna datorisera en verksamhet med fiskar behöver vi objekt av typ Fish. Självklart skulle man kunna skapa sådana objekt t.ex. med Fish f1 = new Fish(); osv. Men vad gör man om man vill modellera en handel med stora fiskmängder under en längre period? Array skulle då vara den givna lösningen för att effektivisera kodningen. Men funderar man närmare på begreppet array av objekt av typ Fish dyker upp följande fråga: Vilket default-värde ska t.ex. en array av Fish-objekt få vid initieringen? Till de enkla datatyperna har C# de fördefinierade default-värdena 0, tom sträng, null, nolltecknet och false (sid 131). Men Fish är ju ingen fördefinierad datatyp. Det finns ingen begränsning på och det går inte heller att förutsäga vilka egendefinierade datatyper (klasser) man kan skapa i C#. Och därför går det inte heller att fastslå vilken default-initiering en sådan array skulle få. Vi ser att begreppet array av objekt leder till en återvändsgränd. Lösningen är array av referenser till objekt en tvåstegslösning som användes ovan. 196

7.6 Array som parameter i metoder Array som bearbetar större datamängder ger upphov till mer komplexa och sofistikerade program. Exempel på det är applikationer som söker, sorterar eller krypterar data. Vi kommer i fortsättningen att behandla enkla varianter av sådana program. Modularisering (sid 119) är metoden för att bryta ned stora komplexa program i mindre och enklare moduler. Helst vill man ha program som består av ett antal enkla, överskådliga metoder där varje metod löser ett specifikt problem. Sedan vill man sätta ihop dem dvs anropa dem med ett antal parametrar från Main() och kontrollera hela händelseförloppet från denna metod som helst ska ha så lite kod som möjligt. Ju mer avancerade datatyper man använder i sitt program desto större blir behovet av modularisering. Självklart vill man även modularisera program som använder array. I C# är det möjligt att skicka en array som parameter till en metod dvs att definiera en array i parameterlistan. I nästa program definieras en void-metod Method() med en array av int som parameter: // ArrayParam.cs // Skickar en stor array till en metod, men: // Array som parameter i en metod behandlas som en referens // Parameteröverföring görs som referensanrop: adressen skickas using System; class ArrayParam static void Method(int[] b) // Array som parameter Console.WriteLine("\n\tI metoden\n\tär arrayens sista " + "element före ändringen " + b[999]); b[999] = 1; // Ändringen Console.WriteLine("\n\t\t\t och efter ändringen " + b[999] + '\n'); /***************************************************************/ static void Main() int[] a = new int[1000]; // Array med 1000 nollor Console.WriteLine("\n\tI Main()\n\tär arrayens sista " + "element FÖRE anropet " + a[999]); Method(a); // Referensanrop: arrayens // adress skickas till metod Console.WriteLine("\tI Main()\n\tär arrayens sista " + "element EFTER anropet " + a[999] + '\n'); Låt oss börja titta på Main() innan vi går in på hur arrayen b i metoden Method() behandlas. I Main() har vi en int-array a med 1000 element, alla initierade till default- 197

värdet 0. En körning av ArrayParam avslöjar även en del intressanta nyheter för oss. Den viktigaste är att en ändring som görs i en annan metod återspeglas i Main(): I Main() är arrayens sista element FÖRE anropet 0 I metoden är arrayens sista element före ändringen 0 och efter ändringen 1 I Main() är arrayens sista element EFTER anropet 1 Som man ser har arrayen a:s sista element a[999] kom ihåg att indexeringen hos arrays börjar med 0 som hade initialvärdet 0, EFTER anropet av metoden fått värdet 1, fast denna ändring inte gjorts i Main() utan i metoden Method(), dessutom med arrayen b och inte med a. Detta verkar bryta mot de regler vi lärt oss om lokala variablers räckvidd (sid 170), därför att a trots allt är en lokal variabel i Main() och därmed inte giltig i Method(). Samma sak gäller för b som är lokal variabel i Method() och därmed inte giltig i Main(). Gåtans lösning är att det handlar endast om en och samma array till vilken a och b är bara två olika referenser. Därför pratar vi i utskriften ovan inte om arrayen a och inte om arrayen b utan om arrayen, för det finns bara en. För att första detta bättre låt oss titta på följande minnesbild som ska förtydliga vad som händer i programmet ArrayParam: Index: 0 1 2 3 998 999 a = 12EFE0 0 0 0 0......... 0 0 1 4 000 bytes a är en referensvariabel som lagrar ett hexadecimalt tal, säg 12EFE0 (decimalt: 1241056) som är arrayens adress. Adresser visas i datavärlden det är en de facto-standard som tal i hexadecimalt format. Med adress menas alltid en plats i datorns RAM-minne (Random Access Memory). När en array definieras lagras den vid en adress och arraynamnet blir en länk mellan programmet och denna fysiska adress. När arrayen a sedan i metodanropet Method(a); skickas som en aktuell parameter, då överförs inte arrayens värb 12EFE0 4 bytes Vi vet att varje int tar 4 bytes i minnesutrymme (sid 62). Därmed tar hela arrayen a med 1 000 int-element 4 000 bytes. Detta stora minnesutrymme allokeras av satsen: int[] a = new int[1000]; 198

den utan arrayens adress till metoden Method(). Denna adress tas emot av den formella parametern b som är definierad i metodens parameterlista som en array av int. På så sätt hamnar a:s adress, det hexadecimala talet 12EFE0 i minnescellen b. Dvs b lagrar a:s adress som tar 4 bytes. Därmed pekar både a och b på en och samma array. Någon kopiering av arrayinnehållet på 4 000 bytes till en ny plats förekommer inte. Endast adressen på 4 bytes kopieras till b vid metodanropet. I Main() kommer man åt arrayen med a och i Method() gör man det med b. När vi sedan i Method() ändrar värdet i arrayens sista element med b från 0 till 1, kan ändringen ses i Main() med a. Den ovan beskrivna metoden för överföring av parametrar kallas referensanrop. Dvs inte parametrarnas värden utan deras adresser överförs vid metodanropet. När parametrarnas adresser överförs och inte deras värden, förekommer ingen fördubbling av minnesåtgång. Alla eventuella ändringar i metoden återspeglas i Main(). Valet av parameteröverföringsmetod styrs av datatypen: I C# väljs automatiskt referensanrop (Call by reference) för parameteröverföring vid metodanrop, om parametern är av datatypen array. Låt oss nu även gå in på med vilken syntax programmet ArrayParam använder en array som en parameter i en metod. 1. Att definiera en metod med array som parameter har gjorts i metoden Method()genom att definiera den formella parametern som en array av int dvs samma datatyp som den aktuella parametern har i anropet: int[] b Antalet element inom hakparentesen får inte anges. Att antalet element inte behövs här beror på att en formell parameter får sitt initialvärde från den anropande metoden. Även arraystorleken följer med vid anropet. Detta har i sin tur att göra med att hela definitionen av en metod endast är en mall, en föreskrift om vad som ska hända om metoden anropas, en potentiell kod som blir aktuell först när vi anropar metoden (sid 150). I metoden Method() står definitionen av parametern b till datatypen array av int som vanligt i parameterlistan och därmed i metodhuvudet: static void Method(int[] b) 2. Att anropa en metod med array som parameter sker genom att skriva den aktuella parametern som array utan hakparenteser i anropet: Method(a); Anmärkningsvärt är att det för första gången dyker upp en array utan hakparenteser. Så, tittar man inte på definitionssatsen några rader ovan kan man inte känna igen a som array. Anledningen till att hakparentesen inte får stå efter arrayen a i anropssatsen är just det vi sade ovan om referensanrop: Anropet skickar inte hela arrayen med dess värden till Method() utan endast referensen a. En hakparentesens skulle tolkas som kod som anger index som specificerar ett visst element i arrayen. En anropssats av typen 199

Method(a[999]); skulle skicka endast ett element av arrayen nämligen det med index 999. Det blir i så fall ett tal av typ int som skickas till metoden. Man kommer att få kompileringsfel i alla fall eftersom metodens formella parameter b är definierad som en array av int och inte som en vanlig int. Den enkla datatypen int kan inte konverteras till den sammansatta datatypen array av int. De automatiska typkonverteringsreglerna gäller endast för enkla datatyper. Det tänkbara alternativet Method(a[]); fungerar inte heller av samma anledning: Det handlar om en icke-definitionssats där hakparentesens innehåll tolkas som index. Men index får aldrig utelämnas (se punkt 1). För att skicka en array som parameter till en metod måste alltså arrayen i metodanropet skrivas endast med arraynamnet utan hakparentes. Självklart måste arrayen innan anropet vara definierad i Main() som vanligt med hakparentes och en uppgift om storleken. Arraynamnet används vid anropet som adressen till arrayen. 200

7.7 Sökning och sortering Ett viktigt numera självklart användningsområde för datorer är sökning i och sortering av stora datamängder. Programmeringstekniskt sett kan sådana applikationer inte skrivas utan arrayer (eller högre datatyper). Därför är sökning och sortering klassiska tillämpningar för sammansatta datatyper. Samtidigt ökar behovet av modularisering ju mer avancerade datatyper man använder i sitt program. Nu när vi lärt oss att skicka arrayer som parametrar till metoder, kan vi modularisera program som arbetar med arrayer. Detta är nödvändigt för att koncentrera sig på den egentliga uppgiften nämligen sökning, sortering eller andra applikationer som t.ex. kryptering (kommer att tas upp i nästa avsnitt). När man söker i eller sorterar data finns redan ett material i form av databaser, tabeller eller listor osv. som man använder. För att skaffa ett liknande underlag för våra testprogram har vi valt att låta den i C# inbyggda slumptalsgeneratorn producera materialet och lagra det i en array. Slumptal i en array Eftersom vi i fortsättningen kommer att jobba med flera program som använder slumptal lagrade i en array vill vi skriva en metod som kan användas av alla dessa program. Vi har valt formen av en void-metod för att generera ett antal slumpvärden och tilldela dem till elementen i en array: // RandArray.cs // Ny metod Rand() slumpar fram en array av heltal mellan // a och b, lagrar dem i arrayen no och skriver ut dem // Anropar den gamla metoden RandomNo.Rand() i en loop // för att få ETT slumptal i varje varv using System; class RandArray public static void Rand(Random r, int[] no, int a, int b) Console.Write("\n\t" + no.length + " heltal mellan " + a + " och " + b + " slumpas fram:\n\n\t"); for (int i=0; i < no.length; i++) no[i] = RandomNo.Rand(r, a, b); Console.Write(no[i] + " "); if ((i % 16 == 0) && (i!= 0)) Console.Write("\n\t"); Console.WriteLine("\n\n"); För förståelse av den gamla metoden RandomNo.Rand() hänvisas till RandomNo-klassen (sid 155) samt hantering av slumptal (sid 98). Det nya i koden ovan är att slumptalen lagras i en array som kommer att användas av fler program vilket demonstrerar inte bara 201

modularisering utan även återanvändning av kod. Filen ovan innehåller inte ett fullständigt program utan endast en klass med void-metoden Rand() som har fyra parametrar varav den ena är en array av int, kallad no som lagrar slumptalen. Arrayen deklareras i parameterlistan och tilldelas i kroppen mellan a och b via satsen: no[i] = RandomNo.Rand(r, a, b); som i en for-sats anropar den externlagrade metoden RandomNo.Rand() som i sin tur i varje varv av loopen slumpar fram ett slumptal mellan a och b. Vi har använt denna metod tidigare i andra program (sid 160). for-satsen som anropar metoden skriver ut slumptalen. Antalet arrayelement bestäms i början av Main() i följande program: // SearchTest.cs // Skapar en array och skickar den till metoden Rand() där den // tilldelas slumptal. Ändringen fås tillbaka pga referensanrop. // Den tilldelade arrayen skickas vidare till metoden MySearch() // som söker efter ett inläst tal bland slumptalen using System; class SearchTest static void Main() Random r = new Random(); int a = 1, b = 1000, searchedno; int[] intarray = new int[200]; // Default-initiering RandArray.Rand(r, intarray, a, b); // Slump-tilldelning Console.Write("\tAnge tal som programmet ska söka efter:\t"); searchedno = Convert.ToInt32(Console.ReadLine()); // Sökt tal Search.MySearch(intArray, searchedno); // Anrop av // sökmetoden Även om vi inte gått igenom programmets alla delar klassen Search med metoden MySearch() fattas ska vi titta på en körning för att bättre förstå vad som händer: 200 heltal mellan 1 och 1000 slumpas fram: 237 255 104 898 422 575 712 34 775 299 192 530 442 17 656 344 276 18 929 282 720 967 336 17 934 378 427 667 600 787 581 838 346 525 224 576 710 484 865 211 360 686 858 798 455 501 142 521 138 405 101 747 951 13 889 271 567 88 612 45 796 46 82 989 366 355 832 918 441 728 635 440 801 719 570 35 757 539 563 434 237 Anrop av 907 177 843 334 835 535 981 637 954 657 623 520 468 63 315 252 870 80 101 317 872 728 58 771 662 594 880 444 502 162 676 173 RandArray.Rand() 179 809 890 517 887 303 532 468 852 282 488 719 660 568 981 657 256 784 888 460 463 118 13 180 120 73 673 242 303 538 783 793 982 98 342 660 174 446 13 215 549 281 113 591 241 987 759 95 261 224 836 719 922 217 711 709 444 358 398 815 631 938 166 962 147 696 738 563 874 322 484 811 419 674 912 830 653 423 587 781 962 226 982 80 703 712 519 Ange tal som programmet ska söka efter: 519 Anrop av Det sökta talet 519 är det 200:e elementet bland talen ovan. Search.MySearch() 202

I programmet SearchTest:s Main()-metod finns bara anrop av två metoder samt definition av deras aktuella parametrar och inläsning av det sökta talet. En array av int har definierats med 200 element och tilldelats referensen intarray. I anropssatsen Rand- Array.Rand(r, intarray, a, b); skickas arrayen till metoden. Det anmärkningsvärda är följande: När arrayen intarray som aktuell parameter i anropet överförs till den formella parametern no i metoden RandArray.Rand(), är den definierad och default-initierad till 0-värden. Faktum är att, när parametern är en array, så används referensanrop (sid 194) där den aktuella parametern intarray, och den formella parametern no, endast är två olika referenser till ett och samma minnesområde, till en och samma array. Med intarray definierar vi arrayen i Main() och anropar RandArray.- Rand(). Med no tilldelar vi samma array i metoden RandArray.Rand() slumpvärden som överskriver arrayens default-värden. En sådan arbetsdelning mellan olika metoder kan endast göras med referensanrop. Efter anropet av slumpmetoden läses in ett värde till variabeln searchedno som tillsammans med arrayen intarray skickas till metoden Search.MySearch(). När MySearch() anropas är arrayen intarray både definierad och tilldelad slumpvärden. Sökmetoden får alltså slumptalsvärden som överförs till den formella parametern t. Vid sidan om no är t nu en till minnescell som lagrar arrayen intarray:s adress i detta program. Även den här parameteröverföringen sker med referensanrop. Vid anropet skickas inte värdena i arrayelementen till metoden utan endast adressen som lagras i intarray. I själva verket är det arrayens adress som överförs till MySearch(), tas emot av t och används sedan i sökmetoden för att hitta det sökta talet i arrayen: // Search.cs // Metoden MySearch() tar emot två parametrar: // arrayen t och heltalet s, det sökta elementet // Söker efter den första förekomsten av s bland arrayelementen using System; class Search public static void MySearch(int[] t, int s) int i; for (i = 0; i < t.length; i++) // Söker igenom array t if (t[i] == s) // Sökkriteriet Console.WriteLine("\n\tDet sökta talet " + t[i] + " är det " + (i+1) + ":e elementet" + " bland talen ovan.\n\n"); break; // Bryter for-satsen // när det sökta hittats if (i == t.length) Console.WriteLine("\n\tDet sökta talet finns ej " + "bland talen ovan.\n\n"); 203

Det sökta talet skickas med den aktuella parametern searchedno och tas emot av den formella parametern s. Nu ska vi titta på vad void-metoden MySearch() egentligen gör och hur den hittar eller inte hittar det sökta talet. Arrayen och det sökta talet är givna. Frågan är: finns det sökta talet i arrayen? Om ja, på vilken position? Algoritmen är väldigt rak och enkel och kallas för linjär sökalgoritm: 1. Gå igenom alla element i arrayen dvs sök igenom arrayen t från början till slutet (linjär sökning). 2. Jämför varje element med det sökta talet. Finns likhet med något element, skriv ut ett hittat-meddelande samt elementets position som är lika med index + 1. Har du hittat en likhet avbryt sökningen. 3. Har du gått igenom alla arrayelement utan att hitta någon likhet skriv ut ett ejhittat-meddelande. Denna algoritm hittar endast den första förekomsten av det sökta talet i arrayen och tar inte hänsyn till att det ev. kan finnas flera exemplar av det sökta talet i arrayen. Programmeringstekniskt har vi översatt algoritmens punkt 1 till C#-kod genom att i metoden MySearch() skriva en for-sats som söker igenom arrayen t från index 0 till t.length-1. I denna for-sats finns en if-sats som implementerar algoritmens punkt 2 och i sin tur innehåller två satser: Hittat-meddelandet och break-satsen. En break-sats avbryter alltid den loop eller den switch-sats i vilken den står, här alltså for-satsen. Det är den som enligt anvisningen i punkt 2 gör att programmet endast hittar den första förekomsten av det sökta talet i arrayen. I punkt 3:s implementering den sista ifsatsen i MySearch() utnyttjar vi att for-satsens räknare i är väl definierad även efter for-satsen och att den har kvar det värde den fick där. Om sökningen gått igenom alla arrayelement utan att hitta något element som är lika med det sökta talet, har forsatsens räknare i nått värdet t.length eftersom detta är första värdet som inte uppfyller for-satsens villkor i < t.length. I detta fall avslutas for-satsen utan break med värdet t.length för i så att villkoret till den efterföljande if-satsen blir uppfyllt och skriver ut ett Ej-hittat-meddelande. Bubbelsortering Sökning i och sortering av stora datamängder är klassiska tillämpningar för sammansatta datatyper, speciellt för arrayer. Medan sökning i förra exemplet baserades på en linjär algoritm, bygger sortering på en ny algoritm, även om den har vissa likheter med sökning. Vi ska fortsätta kapitlet om arrayer med en sorteringsalgoritm som är en vidareutveckling av algoritmen för platsbyte av två värden. Vi har i programmet TransPos (sid 78) använt denna algoritm på två tecken: if (char1 > char2) temp = char1; char1 = char2; char2 = temp; 204