Hallo Marc,
Es kommt zwar spät, aber hier ist eine komplette Neuaflage des
Programms. Dabei habe ich eine dynamische Datenstruktur
verwendet
nein, Du baust zwar eine auf, aber Du _verwendest_ sie gerade _nicht_. Es gibt zwei Möglichkeiten:
-
Datei öffnen
In Schleife: Datensatz einlesen + DS verarbeiten (Punkte-Addition)
Datei schließen
Ende.
-
Datei öffnen
In Schleife: DS einlesen und damit verkettete Liste konstruieren
Datei schließen
In Schleife: DS in der verketteten Liste abklappern, dabei DS verarbeiten
In Schleife: DS in der verk. Liste abklappern und allozierten Speicher freigeben
Ende.
Ein Progamm nach Möglichkeit 1 würde quasi nur aus einem Teil (einer Schleife) bestehen, und zu seiner Realisierung wäre überhaupt keine verkettete Liste nötig. Ein Programm nach Möglk. 2 würde dagegen aus drei Teilen (drei Schleifen) bestehen. Im ersten würde die verk. Liste erzeugt, im zweiten einmal zur Verarbeitung der Daten durchlaufen, und im dritten Teil zerstört werden.
Dein Programm arbeitet nach (1), aber Du sollst (2) realisieren. Abgesehen davon, daß nur (2) das Programmieren einer verk. Liste erfordert, was Du ja üben sollst, ist (2) auch die universellere und „sauberere“ Alternative.
Das Programm ist syntaktisch
fehlerfrei, aber das muss ja lange nichts bedeuten.
Ja.
Das wo ich mir unsicher bin, ist das Bezeichnen des
Listenendes mit -1 im Abschnitt der Initialisierung der Liste.
Das Listenende soltest Du dadurch kennzeichnen, daß Du den „next“-Zeiger des letzten Elements auf „NIL“ setzt. „NIL“ ist die einzige POINTER-Konstante; sie ist mit der Adresse 0 belegt. Wenn ein POINTER auf „NIL“ zeigt, bedeutet das definitionsgemäß, daß er _nirgendwohin_ zeigt. Der Test, ob das letzte Listenelement erreicht ist, lautet „IF (Item.next=NIL) THEN …“.
Zu der -1-Sache kann ich nur sagen: Von „Trick-Programmierung“ mit speziellen Bedeutungen spezieller Zahlen à la „die Eingabe von für die Jahreszahl bedeutet, daß der Kundendatensatz gelöscht werden soll“ ist generell abzuraten. In den 70ger Jahren mag so etwas seine Berechtigung gehabt haben; Paradigma: Spare jedes Byte teuren Speicherplatz, das Du einsparen kannst. Diese Zeiten sind jedoch vorbei und das Paradigma ist heute ein anderes: Programmiere maximal sauber, um die Gefahr einer teuren späteren Fehlersuche zu minimieren, und schreibe Software, die so wiederverwendbar, erweiterbar und sicher wie möglich ist, damit komplexe Projekte überhaupt noch beherrschbar sind.
Zudem hab ich Zweifel, ob die Ausgabe der einzelnen
Punktzahlen so richtig funktioniert.
Du könntest einfach auch ein zweites Programm schreiben, daß Dir eine Test-Studenten-Datei erzeugt. Das wäre übrigens bei weitem nicht so anspruchsvoll wie das Lese-Programm (Dateien generieren ist immer easy, Aus-Dateien-Lesen dagegen meistens „knifflig“).
So, Vorwarnung: gleich werde ich mich bei Dir unbeliebt machen…
PROGRAM Einlesen;
Wieso heißt Dein Programm „Einlesen“, es wird doch nicht nur was eingelesen, sondern auch was berechnet und ausgegeben?
uses crt;
CONST Aufgaben = 4;
Teilaufg = 2;
FILENAME = ‚c:\punkte.dat‘;
TYPE tPunkte = ARRAY [1…Aufgaben, 1…Teilaufg] OF REAL;
tStudent = RECORD
Matrikel_NR: LongInt;
Punkte: tPunkte;
END;
pListe = ^tListe;
tListe = RECORD
content : tStudent;
next: pListe;
END;
Wieso nennst Du den zweiten Record „tListe“, wo er doch nur ein Element (Item) der Liste (= Gesamtheit aller Items) ist?
Warum wählst Du für „Content“ und „Next“ so nichtssagende Namen?
VAR Student: tStudent; {Dateieintrag}
Gut!
PunkteDat: File OF tStudent; {Dateivariable}
Nicht so gut! Warum „PunkteDat“ und nicht z. B. „StudentDataFile“?
FirstListItem : pListe; {Erster Eintrag der Liste}
AktListItem : pListe; {Aktueller Eintrag in der Liste}
Wieso ist First_ListItem_ und Akt_ListItem_ nicht vom Typ „pListItem“?
zaehler : INTEGER;
Wieso läßt Du den Variablennamen „zaehler“ nicht darüber Auskunft geben, was die Variable zählt?
punkteeinzeln : REAL;
punktesumme: REAL;
i: INTEGER;
j: INTEGER;
BEGIN
zaehler := 0;
punktesumme := 0;
{Datei öffnen}
ASSIGN(PunkteDat, FILENAME);
RESET(PunkteDat);
{Liste initialisieren}
New(FirstListItem);
AktListItem := FirstListItem;
AktListItem^.content.Matrikel_Nr := -1; { Menge -1
markiert das Ende der Liste}
{Daten auslesen}
while NOT EOF(PunkteDat) DO
begin
Read(PunkteDat,Student);
AktListItem^.content := Student;
Und next möchtest Du nicht setzen?
{ Ausgeben des Eintrags aus der Liste }
WRITELN(‚Matrikel-Nr.:
‚,AktListItem^.content.Matrikel_NR,‘‘);
punkteeinzeln := 0;
FOR i := 1 TO Aufgaben DO
FOR j:= 1 TO Teilaufg DO
punkteeinzeln := punkteeinzeln +
Student.Punkte[i,j];
WRITELN(‚Punkte: ‚,punkteeinzeln,‘‘);
punktesumme := punktesumme+punkteeinzeln;
zaehler := zaehler + 1;
{ Speicher für den nächsten Listeneintrag reservieren }
New(AktListItem^.Next);
AktListItem := AktListItem^.Next;
AktListItem^.content.Matrikel_NR := -1;
end;
{Speicher freigegeben}
while FirstListItem^.content.Matrikel_NR > -1 do
Besser: WHILE NOT (…ListItem^.Next=NIL) DO
siehe Bemerkung oben
begin
AktListItem := FirstListItem^.Next;
Dispose(FirstListItem);
FirstListItem := AktListItem;
end;
{Datei schließen}
CLOSE (PunkteDat);
{Ausgabe Durchscnittspunkte}
WRITELN (‚Durchschnittspunktzahl der Klausur: ‚,punktesumme
/ zaehler:1:2,‘‘);
END.
Anmerkung 1: So wie ich das sehe verkettest Du die ListeItems überhaupt nicht! Bitte denk selbst darüber nach.
Anmerkung 2: Stimmts, die Kritik zu den Variablennamen fandest Du nörgelig? Nicht böse sein, bitte. Jeder, der Software schreibt, kennt die Qual des ständigen „Sich-gute-Namen-Ausdenken-Müssens“ nur zu gut. Oft bekommen Variablen auch erstmal nur „Scherznamen“, die dann später, wenn klarer geworden ist, was die entsprechende Variable oder das entsprechende Objekt (bei objektorientierter Programmierung) für Aufgaben erfüllt, in einen „richtigen“ Namen umgewandelt werden. Hier ist aber auch schon der entscheidende Punkt: _Wenn die Aufgaben klarer geworden sind_. Das Sich-Ausdenken von richtig guten (des _einen_ richtig guten?), d. h. aussagekräftigen Namen, und das Sich-darüber-klar-werden, was eine Variable/ein Objekt tut und was nicht, ist eng miteinander verknüpft. Die Suche nach dem richtigen Namen ist deshalb so schwierig, weil dahinter eine elementare Frage steckt, die Frage _„Was ist es?“_. Hast Du Klarheit über die Bedeutung einer „Entität“ (vornehmes Wort für „Ding“) gewonnen, dann hast Du automatisch auch einen guten Namen, und bist auch der Lösung des Gesamtproblems ein Stück näher gekommen, weil Du etwas _verstanden_ hast. Daher mein Tip: Nimm das „Richtige-Namen-Problem“ ernst (wenn Du gut werden willst). Bemühe Dich, die Was-ist-es-Fragen bestmöglich zu knacken, schreib die Antworten in die Namen, und lerne dabei jedesmal ein Stück mehr über das Wesen des Problems. Beim Namen-Geben geht es nicht um „schönes Aussehen“, sondern um Verstehen.
Abschließend ein Zitat aus einem dicken Schinken zur professionellen C+±Programmierung: Die Auswahl guter Namen ist eine Kunst. Dem kann selbst ich nichts mehr hinzufügen .
Gruß
Martin