![]() |
Listen und wie man sie nutzt
Nachdem ich hier schonmal gute Hilfe zum Thema Datenverwaltung erhalten habe,
gehe ich gleich zu meinem nächsten Problem über, bei dem ich Hilfestellung bräuchte: Ich habe ein Programm geschrieben, dass zur Laufzeit Records in einem Array speichert und damit arbeitet. Nun möchte ich dieses Array durch Listen ersetzen. Ich habe schon einen Ansatz parat, nur weiß ich nicht, ob das so richtig ist und was geändert werden muss. Daher schreibe ich den relevanten Programmcode hier auf und stelle programmbezogene Fragen als Kommentare im Code. Zunächst einmal das Record TDeveloper samt Komponenten, welches nicht verändert werden sollte. Es ist in eine andere Unit ausgelagert:
Delphi-Quellcode:
Nun der interessante Teil:
TName = String[20];
TPerson = record sureName : TName; lastName : TName; end; TDeveloperID = Integer; TDeveloper = record developerID : TDeveloperID; developerPerson : TPerson; developerDescription : TDescription; end;
Delphi-Quellcode:
type
TListe = ^TElement; TElement = record content : TDeveloper; Next : TListe; end; // Ist dies so richtig? Habe mich etwas eingelesen, und da wurde ein Record mit einem Next-Teil // benutzt. Da ich das eigentliche Record nicht verändern will, habe ich es so gelöst. var First : TListe = nil; Last : TListe = nil; Allocations : integer; // Wird benutzt, um im Finalizationteil zu prüfen, ob alle Listenelemente wieder freigegeben wurden function NewElement(person: TPerson; description: TDescription) : boolean; var TempElement, RunPointer : TListe; I: byte; Temp_ID: Integer; begin if developersCount < DEVELOPER_MAX then begin First := nil; // ---- Temporäre ID ----------------------------------------------------------- Temp_ID := DEVELOPER_ID_MIN + DEVELOPER_MIN; // ---- Wenn es schon andere Developer gibt, wird die Temporäre ID inkrementiert, // ---- bis sie einen Wert hat, der noch nicht als Developer ID vergeben ist --- if developersCount > 0 then begin for I := DEVELOPER_MIN to DEVELOPER_MAX do Runpointer := first; While RunPointer <> nil do begin if Runpointer^.content.developerID = Temp_ID then inc(Temp_ID); Runpointer := Runpointer^.Next; end; end; //-------------------------------------------------------------------------------- new (TempElement); TempElement.content.developerID := Temp_ID; TempElement.content.developerPerson := person; TempElement.content.developerDescription := description; TempElement.Next := nil; if first = nil then begin First := TempElement; Last := First; end else begin Last^.Next := TempElement; Last := Last^.Next; end; NewElement := true; end else NewElement := false; end; // Noch etwas: In dem Berspiel, das ich gelesen habe, lieferte die Funktion NewElement einen // Rückgabewert vom Typ TListe und am Ende wurde NewElement := First; gesetzt. // Nur brauche ich eine Funktion, die bei Erfolg true liefert. // Wieso wird NewElement auf First gesetzt und wie löse ich das am besten? |
AW: Listen und wie man sie nutzt
Es gibt doch in delphi die fertige Klasse TList, die alles beinhaltet, was Du für die Verwaltung von Listen brauchst.
Von der brauchst Du nur mehr Deine spezifische Klasse ableiten. |
AW: Listen und wie man sie nutzt
In den Beispielen, die ich gelesen habe, wird TListe so als typ deklariert:
Code:
Da ich das Endergebnis einreichen muss, werde ich mich wohl daran halten müssen.
type
TListe = ^TElement; TElement = record Value : Integer; Next : TListe; end; var First : TListe; {Einstiegszeiger} |
AW: Listen und wie man sie nutzt
Ist die Fragestellung so vorgegeben ? Es geht mir da um das Hütchen :^. Also wären das dann verkettete Listen etc. Dann geht es mehr um die Theorie. In der Praxis stellt Delphi jedenfalls einiges an fertigen Methoden zur Verfügung. TList, wie bereits gesagt und TObjectList. Und dafür braucht man kein ^. Oder etwa doch ? :mrgreen:
|
AW: Listen und wie man sie nutzt
Es kommt darauf an, was Du willst:
Willst Du üben, wie das Programmieren von Listenstrukturen aussieht, dann wirst Du irgend so eine Pointer Struktur brauchen, mit der Du Listen nachbildest. Das kann je nach Bedarf eine einfach oder doppelt verkettete Liste oder ein B-Baum oder was auch immer sein. Wenn Du einfach nur eine Liste brauchst, dann verwende TList oder TObjectlist, da hast Du nichts mehr mir "^" zu tun, die ganze Zeigerlogik ist in der Klasse schon fertig und gekapselt. Schau Dir dazu in der Delphi Hilfe die beiden Klassen an. |
AW: Listen und wie man sie nutzt
Zitat:
|
AW: Listen und wie man sie nutzt
Dann guck mal hier:
![]() |
AW: Listen und wie man sie nutzt
Dann passt das wahrscheinlich in etwa so, wie Du geschrieben hast. Allerdings dient das System, nach dem Du Deinen Code einrückst, eher der Verwirrung des Gegners (und der eigenen), da fühl ich mich überfordert, dem im Detail nachzugehen:
Delphi-Quellcode:
Irgendwie sollte man glauben, dass da eine Riesen Schleife für I programmiert ist. Tatsächlich wird aber in der Schleife nur runpointer := first immer wieder ausgeführt, danach macht das Programm mit dem While ausserhalb der For Schleife weiter. Ich glaube nicht, dass das sehr sinnvoll ist.
if developersCount > 0 then
begin for I := DEVELOPER_MIN to DEVELOPER_MAX do Runpointer := first; While RunPointer <> nil do begin if Runpointer^.content.developerID = Temp_ID then inc(Temp_ID); Runpointer := Runpointer^.Next; end; end; Ein weiterer Hinweis, um lesbaren Code zu erzeugen: statt
Delphi-Quellcode:
ist m.E. ungleich übersichtlicher:
procedure xyz;
begin if Normalfall then begin endlose wurst von Code, um eine Stufe eingerückt, mit vielen Unterblöcken, dass man mühsam zusammenpassende Begin-ends suchen muss end else x:=1 end;
Delphi-Quellcode:
procedure xyz;
begin if sonderfall then begin x:=1; exit end; Endlose Wurst von Code nicht in einem Then-Zweig eingerückt, sondern auf der Hauptebene. end; |
AW: Listen und wie man sie nutzt
Zitat:
[Nachfolgendes Problem selbst behoben können] Zitat:
Ich mache automatisch Einrückungen innerhalb jeder Schleife so wie zwischen allen begin & ends. Aber anscheinend komme ich damit selbst durch einander und es ist zudem zuviel des Guten. Werde dann wohl versuchen, mir ein einheitliches Schema mit weniger Einrückungen anzugewöhnen. |
AW: Listen und wie man sie nutzt
Ich habe jetzt einen anderen Fehler.
Und zwar bekomme ich die Fehlermeldung Zitat:
Delphi-Quellcode:
Ich füge mit NewDeveloper einen developer hinzu.
type
TListe = ^TElement; TElement = record content : TDeveloper; Next : TListe; end; var First : TListe = nil; Last : TListe = nil; Current : TListe; Allocations : integer = 0; function newDeveloper(person: TPerson; description: TDescription) : boolean; var RunPointer : TListe; I: byte; Temp_ID: Integer; begin if developersCount < DEVELOPER_MAX then begin // ---- Temporäre ID ------------------------------------------------------- Temp_ID := DEVELOPER_ID_MIN; //- Wenn es schon andere Developer gibt, wird die Temporäre ID inkrementiert, //-- bis sie einen Wert hat, der noch nicht als Developer ID vergeben ist -- if developersCount > 0 then begin for I := DEVELOPER_MIN to DEVELOPER_MAX do begin Runpointer := first; While RunPointer <> nil do begin if Runpointer^.content.developerID = Temp_ID then inc(Temp_ID); Runpointer := Runpointer^.Next; end; end; end; new (Current); inc(Allocations); Current.content.developerID := Temp_ID; Current.content.developerPerson := person; Current.content.developerDescription := description; Current.Next := nil; if last = nil then begin First := Current; Last := Current; end else begin Last.Next := Current; Last := Current; end; newDeveloper := true; end else newDeveloper := false; end; function developersCount: Integer; var i : integer; begin i := 0; current := first; while current <> nil do begin inc(i); current := current^.Next; end; developersCount := i; end; function getDeveloperByIndex(index: Cardinal): TDeveloper; var temp : TDeveloper; i: Integer; begin i := 0; current := first; while i <> index do begin inc(i); current := current^.Next; end; getDeveloperByIndex := current^.content; // <<====== Hier tritt der Fehler auf. end; In einer anderen Unit lasse ich eine for-Schleife von 1 - developersCount durchlaufen, bei dem ich den developer mit dem Index I durch getDeveloperbyIndex auslesen lasse und einige Werte in einer listbox anzeigen lasse. Die obigen Codeteile sind die einzigen, die ich verändert habe (vorher lief alles über Arrays), von der anderen Unit sollte der Fehler nicht kommen. |
AW: Listen und wie man sie nutzt
So wie Du es programmiert hast, müsste der Index von 0 bis count-1 und nicht von 1 bis count laufen.
In Deiner getdeveloperbyindex Funktion fehlt jede Art von fehlerbehandlung, wenn der Index zu gross ist (wie es eben hier der Fall ist), deshalb läuft das ganze in eine Exception. Eine Schleife, bei der zu Beginn die Durchlaufzahl feststeht, wie in developerbyindex, sollte übrigens immer als for und nicht als while-Schleife realisiert werden, das trägt zum leichteren Verständnis bei. Wenn in der Schleife beim Weiterschalten einmal nil kommt, dann war der Inder zu gross, da gehört eine Fehlermeldung oder was auch immer hinein. |
AW: Listen und wie man sie nutzt
Die whileschleife hatte ich nur zu testzwecken drin.
Habe die Prozedur nun umgeschrieben und auch eine Bereichsprüfung eingebaut.
Delphi-Quellcode:
Nur ist der Fehler ja ein lesefehler, d.h. der Fehler muss ein anderer sein...
function getDeveloperByIndex(index: Cardinal): TDeveloper;
var temp : TDeveloper; i: Integer; begin current := first; if index <= DevelopersCount then for i := 0 to index do current := current^.Next; getDeveloperByIndex := current^.content; end; |
AW: Listen und wie man sie nutzt
Noch einmal: Der Index läuft von 0 bis count-1 !
Du prüfst jetzt, ob index <= Count ist. Wenn Index = Count ist, dann ist Index um 1 zu gross. Zitat:
|
AW: Listen und wie man sie nutzt
Zitat:
Delphi-Quellcode:
Die Funktion liefert jetzt doch garantiert den developer an [index]. Stelle in der Liste.
function getDeveloperByIndex(index: Cardinal): TDeveloper;
var temp : TDeveloper; i: Integer; begin current := first; if index <= DevelopersCount then for i := 0 to index-1 do current := current^.Next; getDeveloperByIndex := current^.content; end; Jetzt habe ich noch
Delphi-Quellcode:
Ich sehe da kein Problem.
procedure TFrmManageDevelopers.refreshDevelopers;
var I : byte; begin LstBxDevelopers.Clear; for I := 1 to developersCount do LstBxDevelopers.Items.Append(developerToStr(getDeveloperByIndex(I))); end; Und die Fehlermeldung rührt ja immernoch von einem Lesefehler her. Ich würde nur gern wissen, wieso. |
AW: Listen und wie man sie nutzt
Records haben im Zeitalter der OO ausgedient.
Ich würde Objekte/Klassen erstellen welche die Daten halten sollen. Mit generics würde ich dann eine TObjectList ableiten, welche die Datenobjekte aufnimmt. Wenn wie OL nun auch noch die Objekte besitzt, dann braucht man sich nicht einmal um das freigeben kümmern. ForEach wird ebenfalls unterstützt. Weitere Überlegung: Warum soviel Code schreiben, welche Fehler enthalten kann, wenn schon alles fertig ist. |
AW: Listen und wie man sie nutzt
Zitat:
Und wo steht, das alles fertig sei? |
AW: Listen und wie man sie nutzt
Überleg einmal, was passiert, wenn es genau ein Listenelement gibt.
Dein Developerbyindex wird genau einmal mit dem Parameter index=1 aufgerufen.
Delphi-Quellcode:
liefert true
if index <= DevelopersCount
Delphi-Quellcode:
-> from 0 to 0, schleife wird genau einmal durchlaufen
for i := 0 to index-1 do
Delphi-Quellcode:
-> first^.next = nil, weil es ja nur ein Listenelement gibt, der next pointer des ersten Listenelemts zeigt also auf kein weiteres.
current := current^.Next;
current^.content = nil^.content = exception. Im Posting 11 habe ich Dir schon geschrieben: Zitat:
Wenn Du das ERSTE Listenelemnt haben willst, musst Du 0 mal in der Schleife weitergehen, weil dzu Beginn zeigt Dein current ja schon auf das erste Element. Du durchläufst die Schleife prinzipiell einmal zu oft, und fliegst deswegen am Ende hinaus.# |
AW: Listen und wie man sie nutzt
Zitat:
Delphi-Quellcode:
Sollte dann ja die richtige Variante sein.
function getDeveloperByIndex(index: Cardinal): TDeveloper;
var temp : TDeveloper; i: Integer; begin current := first; if (index <= DevelopersCount) and (DevelopersCount > 1) then for i := 1 to index-1 do current := current^.Next; getDeveloperByIndex := current^.content; end; Nur tritt der von mir beschriebene Fehler immernoch auf... |
AW: Listen und wie man sie nutzt
Was spricht den gegen die Verwendung des Delphi-Tags in euren Beiträgen? Bei Delphicode sollte dieser immer verwendet weden
|
AW: Listen und wie man sie nutzt
Dir ist schon klar das die Funktion nicht das Element aus der Liste zurückgibt, sondern nur eine Kopie davon? Wird die Funktion aufgerufen wenn die Liste keine Elemente enthält, wird auf jeden Fall eine Zugriffsverletzung auftreten. Ansonsten mit dem Debugger Schritt für Schritt ausführen.
So würde ich das lösen:
Delphi-Quellcode:
Allerdings ist eine verkettete Liste für Zogriffe über einen Index wenig geeignet.
type
PDeveloper = ^TDeveloper; function getDeveloperByIndex(index: Cardinal): PDeveloper; begin current := first; while (index > 0) and Assigned(current) do begin current := current^.Next; Dec(index); end; if current <> nil then Result := @(current^.content) else Result := nil; end; |
AW: Listen und wie man sie nutzt
Sorry, war nicht am Ende des Threads ....
|
AW: Listen und wie man sie nutzt
Sicher, beim letzten Element der Liste wird es das. Aber er fragt genau dies ja im Anschluss ab.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:16 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz