INF 1000 LØSNINGSFORSLAG TIL UKEOPPGAVER FOR UKE 5 1) Setningen er kompakt skrivemåte for int[] a; a = new int[50]; hvor den første setningen deklarerer arrayen a, og den andre setningen oppretter et array-objekt av lengde 50 og setter a til å peke på dette objektet (som nevnt på forelesningen i uke 5 inneholder en array-variabel egentlig en peker - en adresse - til et array-objekt). 2) a) Ulovlig b) Lovlig, vi får en int-array verdier av lengde 50 c) Ulovlig d) Ulovlig e) Ulovlig f) Lovlig (se kommentar nedenfor). g) Lovlig - vi får en double-array x av lengde 5 med verdiene 1.0,..., 5.0 h) Lovlig - vi får to double-arrayer x og y (se forøvrig kommentar nedenfor) Kommentar til punkt f: Denne setningen gjør tre ting: (1) det deklareres en double-array x;(2) det opprettes et array-objekt av lengde 5 som fylles med verdiene 1, 2,..., 5; og (3) x settes til å peke på array-objektet. Men hvordan vet Java at array-objektet som skal lages skal være av typen double og ikke av typen int? For å belyse dette, betrakt følgende to setninger: int[] a = {1, 2, 3, 4, 5; double[] x = {1, 2, 3, 4, 5; Begge disse er lovlige. I det første tilfellet vil Java lage et int-arrayobjekt, og i det andre tilfellet vil Java laget et double-array-objekt. Betyr det at Java i siste setning starter med å lage et int-array objekt og deretter konverterer dette til et double-array objekt? Nei - vi så jo i punktet over (punkt e) at Java ikke tillater at double[] x settes til å peke på et int-array objekt. Det som skjer er at Java titter på arraydeklarasjonen for å se hvilken datatype (int eller double) som forventes. Bak kulissene kan vi tenke oss at Java erstatter den første setningen over med int[] a; a = new int[]{1, 2, 3, 4, 5; og den andre setningen med double[] x; x = new double[]{1, 2, 3, 4, 5; Kommentar til punkt h: Her får vi deklarert to double-array variable. Det finnes en annen skrivemåte i Java for å deklarere arrayer: vi kunne f.eks. skrevet 'double[] x' som 'double x[]'. Folk som har vært borti visse andre programmeringsspråk liker av og til den siste godt. Men vær da klar over at 'double x[], y' betyr at vi får en double-array variabel x og en enkelt double-variabel y. Det betyr at en enkelt setning kan deklarere variable av ulike typer - og det gjør programkoden vanskeligere å lese. 3) a) Det settes av plass til 10 * 100 = 1000 int-verdier. b) Det settes av plass til 1 * 10 = 10 int-verdier. c) Det settes av plass til 9 * 9 = 81 int-verdier. En kommentar til punkt b: legg merke til at det er lovlig å opprette en to-dimensjonal array med en enkelt rad (tilsvarende for kolonne). Verdiene i arrayen a2 ligger i a2[0][0], a2[0][1],..., a2[0][9] 1
Vi kunne til og med laget en array med plass til bare en enkelt verdi: int[] a = new int[1][1]; Verdien ligger da i a[0][0]. Hva er poenget med dette? Hvis vi på forhånd vet at vi bare trenger en en-dimensjonal array, så er det (i de fleste tilfeller) naturlig å deklarere array-variabelen som en en-dimensjonal array. Men ofte er det ikke klart hvor stor en array skal være før programmet kjører. F.eks. kunne vi i oblig 2 tenke oss muligheten av at programmet først spør brukeren om hvor mange rader og kolonner rutenettet over oljefelter skal ha, og da vil vi ikke at programmet skal krasje om brukeren angir at det bare skal være en enkelt rad og/eller kolonne. Dessuten er det ingen spesielle grunner til at Java ikke skulle tillate oss å lage array-objekter av lengde 1. 4) Program: class LesOgFinnOrd { String[] ordene = new String[100]; int ant = 0; // INNLESING AV ORD String ord = ""; while (!ord.equals("stopp")) { System.out.print("Skriv et ord: "); ord = tast.inword(); ordene[ant] = ord; ant = ant + 1; // Nå ligger ordene i arrayen som: // ordene[0],..., ordene[ant-1] // SØKING ETTER ORD String sokeord = ""; while (!sokeord.equals("stopp")) { System.out.print("Søkeord: "); sokeord = tast.inword(); if (!sokeord.equals("stopp")) { // Løp gjennom arrayen for å se om ordet finnes der int k = 0; boolean funnet = false; while (k < ant &&!funnet) { if (ordene[k].equals(sokeord)) { funnet = true; k = k + 1; if (funnet) { System.out.println("Ordet ble funnet!"); 2
else { System.out.println("Ordet ble ikke funnet."); // end if // end while // end main Kommentarer til programmet: a) Rett før den første while-løkka står setningen String ord = ""; Her er det viktig å initialisere variabelen ord, for ellers vil ord være udefinert når løkke-testen utføres, og da vil programmet krasje. b) Under innlesning av ord (den første while-løkka) kunne det vært lagt inn en ekstra betingelse i løkke-testen for å hindre at programmet krasjer hvis brukeren forsøker å lese inn mer enn 100 ord. Dvs vi kunne erstatte med 5) Program: while (!ord.equals("stopp")) {... while (ant < 100 &&!ord.equals("stopp")) {... c) Under søking etter ord brukes det en ny String-variabel 'sokeord'. Vi kunne naturligvis også ha brukt variabelen 'ord' som nå står ledig etter den første while-løkka. Slik gjenbruk av variable til nye formål bør i regelen unngås. Det gjør programmene vanskeligere å lese, men viktigere: det lager en unødvendig kopling mellom de to hoveddelene av programmet (innlesing av ord og søking etter ord). Hvis vi f.eks. senere finner ut at den første løkka skal bruke en annen variabel enn 'ord' og sletter deklarasjonen av 'ord', så virker plutselig ikke den andre løkka lenger. Situasjonen er naturligvis en annen når det gjelder de variablene som danner datastrukturen i programmet. I programmet ovenfor er det variablene 'ordene' og 'ant' som definerer datastrukturen, og disse brukes gjennom hele programmet. Variabelen 'tast' benyttes også gjennom hele programmet. d) Legg merke til at vi under søking etter ord leter i arrayen så lenge to betingelser er oppfylt: - vi har ikke letet gjennom hele arrayen: k < ant - vi har ikke funnet det vi leter etter:!funnet Straks en av disse betingelsene brytes, vil (k < ant &&!funnet) bli false, og vi forlater while-løkka. class LesTall { double[] x = new double[6]; // Les inn tallene 3
for (int i=0; i<6; i++) { System.out.print("x[" + i + "] = "); x[i] = tast.indouble(); // Sorter tallene java.util.arrays.sort(x); // Skriv ut gjennomsnittet av tallene double snitt = 0; for (int i=0; i<6; i++) { snitt = snitt + x[i]; snitt = snitt / 6.0; System.out.println("Gjennomsnitt: " + snitt); // Skriv ut medianen av tallene double median = (x[2] + x[3]) / 2.0; System.out.println("Median: " + median); 6) Program: class Kontor { Out skjerm = new Out(); String[] kontorer = {"403", "404", "405", "406", "407", "408"; String[] bruker = new String[kontorer.length]; // Les inn navn på brukerne av kontorene for (int i=0; i<kontorer.length; i++) { skjerm.out("brukeren av kontor " + kontorer[i] + ": "); String s = tast.inline(); if (s.equals("")) { bruker[i] = "ledig"; else { bruker[i] = s; // Skriv ut oversikt over kontorenes brukere int antledige = 0; skjerm.out("kontor", 8); skjerm.outln("bruker"); for (int i=0; i<kontorer.length; i++) { skjerm.out(kontorer[i], 8); skjerm.outln(bruker[i]); if (bruker[i].equals("ledig")) { antledige++; skjerm.outln("antall ledige kontorer: " + antledige); 4
Kommentar til programmet: vi kunne lett klart oss uten String-arrayen 'kontorer' som inneholder nummerne på kontorene. Under innlesningen av brukere kunne vi f.eks. erstattet den første utskriftssetningen i forløkka med skjerm.out("brukeren av kontor " + (403 + i) + ": "); og vi kunne gjort noe tilsvarende når vi skriver ut oversikt over kontorenes brukere. Ulempen med denne løsningen er dels at det er vanskelig å lese ut av programmet hvilke nummer kontorene faktisk har (vi må inn i for-løkkene for å finne det ut), og dels at det er kronglete å endre programmet til å håndtere andre typer kontornr ("huller" i nummereringen, eller at kontornummerne inneholder tekst som f.eks. "A-403", "A-404", osv). 7) Her trenger vi en double-array, men vi vet ikke i utgangspunktet hvor lang denne må være. Nedenfor antar vi enkelt at det ihvertfall ikke er mer enn 1000 tall som skal leses inn. En annen mulighet som vi kom inn på i forelesningen Java 5 er å starte med en array av en gitt lengde, og utvide denne ved behov (se forelesningsnotatene for et eksempel på hvordan dette kan gjøres). Det synes naturlig å lese tallene fra brukeren med indouble(), men da kan ikke brukeren avslutte innlesningen ved å svare blankt (dvs kun taste returknappen), siden dette bare får indouble() til å be om tallet på nytt. En mulig løsning ville være at brukeren oppga tallet 0 (eller et annet avtalt tall) for å avslutte - men denne løsningen har åpenbare svakheter. En annen løsning ville være at programmet før hver innlesing av et tall spør brukeren om det skal leses flere tall (og hvor brukeren f.eks. må svare "ja" eller "nei") - men det er ganske upraktisk for brukeren hvis det skal leses inn mange tall. Begge disse løsningene er forsåvidt greie nok for denne oppgaven, men nedenfor illustreres en tredje mulighet som er mer elegant (hvor brukeren kan avslutte ved kun å trykke retur), men som benytter en metode som ikke er forelest ennå som heter Double.parseDouble(...). class Hoyde { double[] høyder = new double[1000]; int antall = 0; // INNLESNING String svar = "x"; // Initialiser til hva som helst annet enn "" while (!svar.equals("")) { System.out.print("Oppgi høyde: "); svar = tast.inline(); // Les inn svar som tekst if (!svar.equals("")) { høyder[antall] = Double.parseDouble(svar); // Konverter til tall antall++; // BEREGNING AV MINSTE OG STØRSTE double minval = høyder[0]; double maxval = høyder[0]; for (int i=1; i<antall; i++) { if (høyder[i] < minval) { minval = høyder[i]; 5
if (høyder[i] > maxval) { maxval = høyder[i]; // BEREGNING AV GJENNOMSNITT double snitt = 0; for (int i=0; i<antall; i++) { snitt += høyder[i]; snitt = snitt / antall; // BEREGNING AV VARIANS double varians = 0; for (int i=0; i<antall; i++) { varians += (høyder[i] - snitt) * (høyder[i] - snitt); varians = varians / (antall - 1); // Forutsetter at antall > 1 System.out.println("Minste verdi : " + minval); System.out.println("Gjennomsnitt : " + snitt); System.out.println("Største verdi: " + maxval); System.out.println("Varians : " + varians); 6