Du er her: UiO > 中国竞猜网_中国足彩网-足球推荐 > Emner > Matematikk og naturvitenskap > Informatikk > INF1000 > h08 > ukeoppgaver >

L?sningsforslag ukeoppg. 9:  20. - 23. okt (INF1000 - H?st 2008)

Mer om metoder og klasser (kap. 7 - 9), og tips til Oblig 3.
NB! Disse er bare forslag til l?sninger, dine svar kan v?re like gode eller bedre selv om de ser annerledes ut.
Du f?r sannsynligvis mer utbytte av l?sningsforslagene etter at du har fors?kt ? l?se oppgaven selv.  Du kan ogs? finne l?sning p? noen av oppgavene fra l?reboka i bokens hjemmeside: www.universitetsforlaget.no/java.

M?l
Forst? prinsippene for probleml?sning ved hjelp av klasser og metoder; og f? tips til ?debugging? av Oblig 3.

Oppgaver til teoritimen

  1. Dataklubb.java: Probleml?sning med klasser og metoder   (Oblig3-relevant!)

    Bruk noen minutter til ? lese og tenke over hver av disse oppgavene:
    (a) Foresl? en datastruktur for et enkelt system som kan holde orden p? medlemmene i en dataklubb.  Hver medlem har navn, alder, og et favoritt-programmeringsspr?k.  Anta f?rst at systemet bare skal ha to menyvalg: legg til medlem og fjern medlem.  Programmet benytter en datafil som har en linje for hver medlem, p? formen: Fullt navn:alder,spr?k Filen blir lest bare én gang, n?r programmet starter, og overskrives n?r bruker velger 'Avslutt'-kommandoen.  Nedenfor ser du et eksempel p? 3 linjer fra datafilen.  Du kan anta at alle medlemmene har forskjellig navn.  Foresl? hvilke klasser det passer ? lage til et slikt system, og mulige koblinger mellom klassene (vha. pekere, arrayer, eller HashMap-er), slik at de to menyvalgene blir enkle ? programmere.  Mer info: Lysark 9, s. 22 - 25.
    Ida Lund:19,Java
    Kaja Moen:20,C++
    Hans Holt:21,Java
    
    Et alternativ er:
    • Klasse Dataklubb: med en HashMap hvor n?kkel er medlemsnavn og verdi Medlem.
    • klasse Medlem: med 3 objektvariabler: navn, alder, spr?k

    (b) Datastruktur for lister: Anta n? at systemet skal ha et menyvalg som sp?r bruker om et dataspr?k og skal vise en liste over medlemene som har det som favorittspr?k.  Foresl? en datastruktur (klasser, objektvariabler, og klasse-koblinger) som gj?r det enkelt ? skrive ut en slik liste.  Anta deretter at liste-kommandoen i stedet skal vise alle medlemmene, men sortert p? navn, og at man vil gj?re dette menyvalget enkelt ? programmere ved ? alltid holde personobjektene i datastrukturen sortert p? navn.  Hvilke klasser og klasse-tilkoblinger passer det ? velge da?  Foresl? en m?te ? holde listen sortert p?.
    0 - Avslutt
    1 - Legg til medlem
    2 - Fjern medlem
    3 - Liste over medlemmene
    
    I f?rste tilfelle kan det l?nne seg ? innf?re en klasse Spr?k, med en HashMap som objektvariabel hvor navnene til medlemene som har det spr?ket som favoritt legges inn.  Man trenger da ogs? en array eller HashMap i klassen Dataklubb som refererer til alle Spr?k-objekter som er blitt opprettet.  Disse HashMap-ene kan erstatte HashMap-en som ble nevnt i (a), eller man kan beholde begge sett med HashMap-er og alltid holde begge oppdatert n?r medlemer legges til/fjernes.

    I andre tilfelle kan man innf?re en array av Medlems-pekere i klassen Dataklubb, fordi arrayer har en gitt rekkef?lge som man kan ordne i programmet.  HashMap-er har ikke noen slik bestemt rekkef?lge, men man kan f? til noe lignende ved ? bruke tall som n?kkel (konvertert til String, eller av type Integer).  Arrayen eller HashMap-en kan holdes sortert ved ? plassere nye medlemmer der navnet deres passer i rekkef?lgen og "skyve" de som sto etter én plass freme.  Man kan velge ? beholde HashMap-en nevnt i (a) i tillegg til denne nye arrayen/HashMap-en, eller bare bruke det siste.  Begge valg har sine fordeler og ulemper, f.eks. innlegging og fjerning av medlemmer m? hver gang oppdateres p? to m?ter hvis man velger begge deler; p? en annen side, hvis man bare bruker array/HashMap med tall-n?kkel s? blir det vanskeligere ? sl? opp medlemmer vha. navn.

    (c) Lese og skrive datafilen: Ta utgangspunkt i et av datastrukturene du foreslo i del (a) eller (b), og programmér en metode som leser datafilen (og overf?rer dataene til den valgte datastrukturen).  V?r obs p? skilletegnene!  Programmér ogs? en metode som skriver samme datafil.  I hvilken klasse passer det ? ha disse metodene?  Angi hvordan lesing av datafilen kan programmeres b?de i tilfellet n?r klassen Medlem (evt. kalt Person) ikke har konstrukt?r, og n?r den har en konstrukt?r med parametre for alle objektvariablene i klassen.  Mer info: Lysark 9, s. 4 - 8.
        Dataklubb() {
    	In innfil = new In("klubbdata.txt");
    	while (!innfil.endOfFile()) {
    	    Medlem m  = new Medlem();
    	    m.navn = innfil.inWord(":");
    	    m.alder = innfil.inInt(":,"); // M? ha med begge skilletegnene
    	    m.spr?k = innfil.inWord(",");
    	    // Hvis man bruker HashMap:
    	    medlemHash.put(m.navn, m);
    	}
    	innfil.close();
        }
    
        void avslutt() {
    	Out utfil = new Out("klubbdata.txt");
    	for (Medlem m : medlemHash.values()) {
    	    utfil.outln(m.navn + ":" + m.alder + "," + m.spr?k);
    	}
    	utfil.close();
        }
    

    (d) Hva slags feilmelding vil man f? fra easyIO i del (c) hvis man kj?rer programmet n?r metoden som leser datafilen er ferdig programmert mens metoden som skriver datafilen bare er skrevet halvveis?  Anta at metoden som leser datafilen ikke foretar noen spesiell forh?ndssjekk av filen og i stedet bare g?r rett p? og pr?ver ? lese den.  Idéen her er ? l?re ? kjenne igjen typiske feil man vi f? under arbeidet med Oblig 3.  Vurdér f?lgende fire muligheter, og foresl? en enkel og effektiv "debuggings"-strategi:
    • datafilen ikke eksisterer;
    • datafilen finnes men er helt tom;
    • datafilen finnes og har alle navn, men mangler alder;
    • datafilen finnes og har alle dataene, men mangler én komma.
    I f?rste tilfelle er feilmeldingen:
      Feil i metoden In(filnavn): klubbdata.txt (Systemet finner ikke angitt fil)
      Programmet avsluttes.

    I 2. tilfelle blir det ingen feil fordi l?kken kommer aldri i gang siden endOfFile() er true med én gang filen ?pnes.

    I 3. og 4. tilfelle f?r man feilmeldinger som denne:
      Feil: Ved lesing av et heltall, linje 0: leste "Java".
      Programmet avsluttes.

    Linjenummeret "0" refererer til f?rste linje i datafilen, ikke i programmet.  Trikset for ? dubugge slik innlesing/utskrift til fil er ? legge inn i l?kken som leser fra fil utskriftssetninger som skriver ut dataene som blir lest inn i hver gjennomomgang av l?kka.  Da vil man lett kunne se hvor mye som ble lest fra datafilen, og hvor et evt. problem oppsto.  I tillegg s? er det nyttig ? sjekke n?v?rende innhold i datafilen vha. more filnavn.txt p? Linux-kommandovinduet, og type filnavn.txt p? Windows-kommandovinduet.

    (e) Anta at systemet har en ordrel?kke og at hver ordre har en tilh?rende metode.  I hvilken klasse ville du passert hovedmetodene for hver ordre?, og hvor ville du plassert eventuelle hjelpemetodene for disse?  Skriv den f?rste ordre-metoden nevnt i (b) ovenfor, som sp?r bruker om et dataspr?k og skriver ut listen over klubbmedlemmene som har det som sitt favorittspr?k.  Metoden skal ta i bruk en hjelpemetode i en annen klasse.  Nevn fordeler av ? ha en slik hjelpemetode (f.eks. med hensyn til gjenbruk, bruk selv om man ikke vet detaljene i innmaten, tenke operasjon/verkt?y med bare input/output, samle kode som omhandler en klasse, deling av programmet i mindre biter, osv.).  Mer info: Lysark 9, s. 20 - 25.
    Ordre-metoder passer best i Dataklubb fordi hoved-datastrukturen (enten array eller HashMap) er deklarert i den klassen og er datastrukturen som ordrene trenger f?rst.

    Hjelpemetoden kan deklareres som void skrivMedlemmer() hvis den plasseres i klassen Spr?k (som nevnt i f?rste l?sningsforslag til del (b) over).  Hjelpemetoden kan kalles fra ordre-metoden void skrivListeOverMedlemmene() rett etter at man ber bruker taste inn spr?ket og har sl?tt opp spr?k-objektet.  Kallet utf?res vha. dette spr?k-objektet, f.eks. spr?k.skrivMedlemmer();.&nbps; Hvis man ikke har egen Spr?k-klasse kan hjelpemetoden plasseres i klassen Dataklubb

    Mulig kode i ordre-metoden:
        void skrivListeOverMedlemmene() {
    	skjerm.out("Angi dataspr?k: ");
    	String spr?kNavn = tast.inWord();
    	Spr?k spr = spr?kHash.get(langNavn);
    	spr.skrivMedlemmer();
        }
    


    (f) N? skal vi se p? metodene som legger til og fjerner et medlem av klubben.  Begge disse starter med ? be bruker om ? taste navnet p? medlemmet som skal legges til eller fjernes.  Vurdér om en eller begge av disse metodene ville bli forenklet hvis man i tillegg programmerte en hjelpemetode finnStudent(..).  Foresl? inn- eller ut-parametre som du ville hatt i en slik hjelpemetode.  Hvordan kan kallet p? hjelpemetoden gj?res uavhengig av datastruktur (f.eks. slik at kalleren ikke trenger ? vite om implementasjonen bruker HashMap eller array).  Er det lurt ? plassere hjelpemetoden i klassen Medlem?
    Ja, hovedmetodene som utf?rer ordrene for ? legge til og fjerne medlemmer kan forenkles, og trenger ikke vite om dataene er lagret i arrayer eller HashMap-er hvis man lager en hjelpemetode "Medlem finnStudent(String navn)" som mottar medlems-navnet som inn-parameter, og returnerer peker til medlemmets objekt (eller null hvis det ikke fantes).  Da kan man anse hjelpemetoden som et lite verkt?y som sl?r opp i den aktuelle datastrukturen (p? en passende m?te avhengig av om dataene er lagret i arrayer, HashMap-er, e.l.).  Alle de nevnte fordelene gjelder da, og metoden kan da brukes fra enda flere steder i programmet.  Det passer ikke s? bra ? plassere denne hjelpemetoden i klassen Medlem fordi klassen Medlem bare har direkte tilgang til data om ett medlem, mens finnStudent(..) trenger tilgang til alle.



  2. Gruppe.java: Feilmeldinger fra Java  (Oblig3-relevant!)

    Finn feilene i f?lgende program og foresl? hvordan de kan rettes.  Programmet er et veldig enkelt system som kan holde orden p? studentene i en INF1000-gruppe.  Det best?r av to klasser: Student, som bare har én objektvariabel: navn; og Gruppe, som har en array med opptil 40 studenter, og en konstrukt?r hvor all testkode er lagt inn.  Feilene som vises er typiske feil som du vil komme bort i under arbeidet med Oblig 3.

    Generell debuggings-tips: B?de feilmeldingene fra kompilatoren (javac) og kj?resystemet til Java (java) gir deg vanligvis linjenummeret der feilene er, og navnet til feilen.  Kompilatoren javac markerer i tillegg hvor p? selve linja feilen sannsynligvis ligger.  Alle disse opplysningene er veldig nyttige n?r du skal finne og rette feilen.  Start alltid med ? rette den f?rste feilen som Java fant.
    class Student {
        String navn;
    }
    
    class Gruppe {
        Student[] studenter = new Student[40];
        int antStudenter; // Antall plasser i bruk av arrayen over
    
        public static void main(String[] args) {
    	Gruppe g = new Gruppe();
        }
    
        Gruppe() {
    	/* a) Den vanligste feilmeldingen man f?r n?r man programmerer i Java
    	 *    er "cannot find symbol".  Denne typen feil skyldes at noe ikke
    	 *    er deklarert (eller at noe ikke er deklarert p? riktig sted og
    	 *    med riktig stavet navn).  Feilen er veldig lett ? fikse: se i
    	 *    feilmeldingen hva det var som ikke var deklarert (det st?r ved
    	 *    "symbol", i eksemplet under er det "variable antallStudenter").
    	 *    Se ogs? linjenummeret og posisjonen p? linja som Java angir.  I
    	 *    dette tilfellet sier Java at feilen er i begynnelsen av linje 30.
    	 *    Oppgave: Hvordan kan vi fikse denne feilen?
    	 *
             *                 Gruppe.java:30: cannot find symbol
             *                 symbol  : variable antallStudenter
             *                 location: class Gruppe
             *                         antallStudenter = 0;
             *                         ^
             *                 1 error                             */
    	antallStudenter = 0;
    
    	// b) F?lgende linje gir NullPointerException. Hva mangler ? gj?re her?
    	studenter[antStudenter++].navn = "Trine";
    
    	// c) Fyll inn det som mangler for at studenter[1].navn blir "Martin".
    	Student s1 = new Student();
    	_________ = "Martin";
    	studenter[antStudenter++] = _________;
    
    	// d) Hva mangler her for at if-testen skal kunne gi true?
    	if (studenter[1].equals("Martin")) {
    	    System.out.println(true);
    	}
    
    	// e) Hvorfor gir f?lgende l?kke NullPointerException i siste linje?,
    	//    og hvordan kan betingelsen i f?rste linje endres for ? unng? det?
    	for (int i = 0; i < studenter.length; i++) {
    	    Student s2 = studenter[i];
    
    	    System.out.println(s2.navn);
    	}
    
    	// f) Finn en annen m?te ? unng? NullPointerException p? uten ? endre
    	//    linjene som st?r skrevet her, men ved ? legge til en annen linje:
    	for (int i = 0; i < studenter.length; i++) {
    	    Student s3 = studenter[i];
    
    	    System.out.println(s3.navn);
    	}
    
    	// g) Hva kan ha st?tt her hvis linjen ga feilmeldingen
    	//    "ArrayIndexOutOfBoundsException: 40":
    	studenter[___] = new Student();
    
    	// h) Hva kan ha st?tt her hvis linjen ga feilmeldingen
    	//    "ArrayIndexOutOfBoundsException: -1":
    	s1 = studenter[___];
    
    	// i) Hva er feil her? Feilmeldingen er: incompatible types
    	//                  found   : java.lang.String
    	//                  required: Student
    	studenter[2] = "Eva";
    
    	// j) Hva er feil her? Feilmeldingen er: cannot find symbol
            //                  symbol  : constructor Student(java.lang.String)
    	Student s4 = new Student("Lars");
    
    	// k) Hvorfor gir linjen med "if" under feilmeldingen: incompatible types
    	//                  found   : int
    	//                  required: boolean
    	//    Hvorfor gir Java feilmeldingen: "package system does not exist"
    	if (antStudenter = 0) {
    	    system.out.println("Ingen student registrert!");
    	}
    
    	// l) N?r vi har rettet alle feilene i a) til k) ovenfor, s? gir fortsatt
    	//    f?lgende "if" NullPointerException.  Hvordan kan vi unng? det?
    	boolean funnet;
    	for (int i = 0; i < studenter.length; i++) {
    	    Student s5 = studenter[i];
    	    if (s5.navn.equals("Eva")) {
    		funnet = true;
    		System.out.println(s5.navn);
    	    }
    	}
    
    	// m) L?kken ovenfor stopper ikke n?r navnet "Eva" blir funnet.
    	//    Hvordan kan vi f? l?kken til ? stoppe n?r navnet blir funnet?
    
    	// n) L?kken ovenfor gir ingen melding til bruker n?r navnet ikke blir
    	//    funnet.  Hvordan kan vi programmere utskrift av en slik melding?
    	//    Og hvordan kan vi unng? at Java da skal klage om at "variable
    	//    funnet might not have been initialized"?
    
    	// o) Anta at navnet til studenter[2] er "Eva".  Hvorfor endrer ikke
    	//    f?lgende kode navnet til studenter[2]?  Hvordan kan det ordnes?
    	//    (slik at studentobjektet i "ny" overf?res til studenter[2]).
    	Student ny = new Student();
    	Student sPeker;
    	ny.navn = "Heidi";
    	sPeker = studenter[2];
    	sPeker = ny;
        }
    }
    
    a)  antStudenter er stavet feil.
    b)  Det mangler f?lgende linje rett f?r linjen som st?r i oppgaven:
          studenter[antStudenter] = new Student();
    c)  Student s1 = new Student();
        s1.navn = "Martin";
        studenter[antStudenter++] = s1;
    d)  Det mangler .navn:  if (studenter[1].navn.equals("Martin")) {
    e)  Her blir det NullPointerException fordi de fleste plasser i arrayen
        ikke har noe Student lagt inn.  En l?sning er ? endre betingelsen i
        for-l?kke slik at i bare teller opp til antStudenter:
          for (int i = 0; i < antStudenter; i++) {
    f)  En annen l?sning er ? legge til en if-setning:
          for (int i = 0; i < studenter.length; i++) {
    	  Student s3 = studenter[i];
    	  if (s3 != null) {  // Nytt!
    	      System.out.println(s3.navn);
              }
          }
    g)  Studenter[ 40 ] = new Student();
    h)  s1 = studenter[ -1 ];
    i)  Det mangler .navn:  studenter[2].navn = "Eva";
    j)  "new Student("Lars");" fors?ker ? kalle en konstrukt?r med en
        String-parameter, men klassen Student har ingen slik konstrukt?r.
    k)  Fordi if-setningen har bare ett "="-tegn, der det skulle st?tt "==".
        Hele uttrykkt i parentés blir dermed en tilordning, som gir verdien
        int 0 i if-betingelsen.  Det gir feil fordi Java krever at
        if-betingelser m? v?re av typen boolean.  Med "==" ville det v?rt
        en sammenligning i if-setningen, som er ok.
    l)  Legge til en betingelse til i if-setningen, f.eks.:
        if (s5 != null && s5.navn....osv....)
    m)  Legge til f?lgende i selve for-l?kkens betingelse: && !funnet
    n)  Sette startverdien "false" i funnet, og legge til rett etter
        l?kken en if-setning som skriver ut en melding om "ikke funnet"
        hvis !funnet.
    o)  Vi har 2 student-objekter i minnet: Eva som er pekt p? av studenter[2],
        og Heidi som er pekt p? av ny.  Det som kodebiten gj?r er ? sette
        sPeker til ? peke p? det f?rste, og deretter flyttes (bare) denne
        sPeker til ? peke p? ny i stedet.  Det endrer ikke p? hva pekeren
        studenter[2] peker p?.  For ? ordne det m? man bruke en tilordning
        hvor studenter[2] st?r p? venstre side av "="-tegnet, f.eks. ved
        ? legge til f?lgende som en ny linje rett etter kodebiten i oppgaven:
          studenter[2] = sPeker;
    
    Tips til kr?llparentes-feil:
    En annen type feil som er lett ? gj?re i Java er kr?llparentes-feil, f.eks. ? glemme en kr?llparentes et sted, ha en for mye, en som "vender" feil vei, o.l. Disse problemene gir opphav til mange typer feilmeldinger, bl.a.:
    • ?class, interface, or enum expected?, ?<identifier> expected?
    • ?reached end of file while parsing?, ?'}' expected?,
    • ?illegal start of type?, ?illegal start of expression?, ?'else' without 'if'?, m.fl...

    To gode tips for ? unng? slike feil er:
    1. Alltid holde programmet fint formatert med "innrykk" underveis mens man skriver det: Start et nytt "niv?" med innrykk rett etter hver "{" (?pnings-kr?llparentes) du skriver, dvs. at alle linjene etter en slik kr?llparentes f?r f.eks. 4 flere mellomrom foran enn linjene f?r den "{"; ...frem til en "}", da man forminsker ett niv?, osv.  Alle programbiter i disse ukeoppgavene er formatert p? denne m?ten.
    2. N?r et av de ovennevnte feilene dukker opp, bruke funksjonen i Emacs (eller Eclipse, e.l.) som setter inn riktig innrykk i hele programmet. I Emacs kan det gj?res ved ? markere hele programmet og velge "Java > Indent Line or Region" fra menyen ?verst i Emacs-vinduet.  Da blir det lett ? se hvor feilen er, ved ? sjekke at alle klasser havnet i "f?rste innrykks-niv?" (dvs. at det ikke er noen mellomrom rett f?r n?kkelordet class), og at f?rste linje i alle metoder havnet i 2. niv? av innrykk (dvs. at det er 4 mellomrom rett f?r n?kkelordet void), og at innmaten i metodene er i 3. niv? (har 8 mellomrom foran).

    Enda flere tips til feils?king og -retting: Se Marit Nybakkens feilmeldinger.pdf



Oppgaver til terminaltimen

  • Gruppe.java: Feilmeldinger fra Java  (Oblig3-relevant!)
    (Samme som punkt 2. for teoritimen)


  • Gj?r ferdig Oblig 3.
    (Husk oraklene denne uken!, se kurshjemmesiden)


  • Ukens n?tt:  (Oblig4-relevant!)
    Samme n?tt som forrige uke, men n? bruker vi tips 1. p? side 7 i oppgaveteksten til Oblig 4.  Det tipset kan brukes til ? utf?re den "indre" sorteringen p? navn, men den "ytre" sorteringen p? alder er fortsatt like utfordrende som f?r!
  • Send gjerne l?sningen din til josek@ifi.uio.no s? legger jeg den ut her.