Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi Name aus Liste löschen (https://www.delphipraxis.net/56990-name-aus-liste-loeschen.html)

der olli 14. Nov 2005 18:36


Name aus Liste löschen
 
Hi!

Wir realisieren im Informatikunterricht z.Z. eine Liste mit Namen, die eingegeben und gelöscht werden können (alles mit Hilfe von Zeigern), folgendermaßen aufgebaut: Zwei Listboxes (eine für die Platzierung, da es sich bei der Liste um Skispringer handelt und für den Namen und eine für die zum Namen zugehörige Wertung) und zwei Buttons, nächster Springer und Springer löschen. Bei Klick auf nächster Springer erscheinen zwei InputBoxes, zuerst eine für den Namen und danach eine für die zugehörige Wertung. Nach diesen Einträgen werden der Name und die Wertung gespeichert und sortiert nach Wertung in die jeweiligen Listboxes eingetragen. Die Procedure hierzu ist folgende (zu oberst die Deklaration der globalen Variablen):
Delphi-Quellcode:
type
  TZeiger = ^TSpringer;

  TSpringer = record
    name   : String;
    wertung : Real;
    vorher,
    nachher : TZeiger
  end;

var
  Skispringen : TSkispringen;
  oben       : TZeiger;
  platz      : Integer;
Delphi-Quellcode:
procedure TSkispringen.bt_naechster_springerClick(Sender: TObject);
var aktuell, hilf, lauf : TZeiger;
begin
 new(aktuell);
 aktuell^.name   := InputBox('Eingabe','Name','');
 aktuell^.wertung := StrToFloat(InputBox('Eingabe','Wertung',''));
 aktuell^.vorher := nil;
 aktuell^.nachher := nil;
 IF oben = nil THEN
  oben := aktuell
 ELSE
 begin
  IF aktuell^.wertung > oben^.wertung THEN
  begin
   oben^.vorher    := aktuell;
   aktuell^.nachher := oben;
   oben            := aktuell
  end
  ELSE
  begin
   lauf := oben;
   WHILE (lauf^.wertung > aktuell^.wertung) AND (lauf^.nachher <> nil) DO
    lauf := lauf^.nachher;
   IF (aktuell^.wertung > lauf^.wertung) THEN
   begin
    aktuell^.vorher := lauf^.vorher;
    aktuell^.nachher := lauf;
    hilf            := lauf^.vorher;
    hilf^.nachher   := aktuell;
    lauf^.vorher    := aktuell
   end
   ELSE
   begin
    lauf^.nachher  := aktuell;
    aktuell^.vorher := lauf
   end
  end
 end;
 ausgabe
end;
Und hier die Procedure zum ausgeben:
Delphi-Quellcode:
procedure TSkispringen.ausgabe;
var hilf : TZeiger;
begin
 li_skispringer.Clear;
 li_wertung.Clear;
 hilf := oben;
 platz := 0;
 WHILE (hilf <> nil) DO
 begin
  inc(platz);
  li_skispringer.Items.Add(IntToStr(platz) + '. ' + hilf^.name);
  li_wertung.Items.Add(FloatToStr(hilf^.wertung));
  hilf := hilf^.nachher;
 end
end;
Soweit, so gut. Klappt auch alles ohne Probleme. Nun habe ich mich eben hingesetzt und den Algorithmus programmiert, um einen Springer anhand seines Namens zu löschen:
Delphi-Quellcode:
procedure TSkispringen.bt_springer_loeschenClick(Sender: TObject);
var lauf : TZeiger;
    such : String;
begin
 such := InputBox('Eingabe','Name','');
 lauf := oben;
 WHILE NOT (lauf^.nachher = nil) AND (such = lauf^.name) DO
  lauf := lauf^.nachher;
 IF (lauf^.name = such) AND (lauf^.nachher = nil) THEN
  lauf^.vorher^.nachher := nil
 ELSE IF such = lauf^.name THEN
 begin
  lauf^.vorher^.nachher := lauf^.nachher;
  lauf^.nachher^.vorher := lauf^.vorher
 end
 ELSE IF such = oben^.name THEN
  oben^.nachher^.vorher := nil;
 // ELSE exit;
 ausgabe
end;
Hier hört das Programm leider auf zu funktionieren. Ich kann zwar den zu löschenden Namen eingeben, aber leider wird dieser nicht aus der Listbox gelöscht und ich komme einfach nicht drauf, wieso nicht. Ich hoffe, ich hab das verständlich erklärt und es kann mir jemand helfen.

Gruß,
Olli

P.S.: Ich weiß, dass sowas auch einfacher zu realisieren ist, aber wir müssen halt mit Zeigern arbeiten ...

Niko 14. Nov 2005 19:08

Re: Name aus Liste löschen
 
Hi,

der Fehler liegt hier:
Delphi-Quellcode:
WHILE NOT (lauf^.nachher = nil) AND (such = lauf^.name) DO
Funktionieren sollte es mit einem zweiten NOT nach dem AND oder einfacher so:
Delphi-Quellcode:
WHILE (lauf^.nachher <> nil) AND (such <> lauf^.name) DO

der olli 14. Nov 2005 19:23

Re: Name aus Liste löschen
 
Vielen Dank, jetzt funktioniert's! Ich wusste nicht, dass ich das NOT zweimal verwenden muss - wieder was gelernt, danke!

Gruß,
Olli

Phistev 14. Nov 2005 19:46

Re: Name aus Liste löschen
 
Statt such = lauf^.name würde ich hier AnsiCompareText verwenden, dann spielt die Groß-/Kleinschreibung keine Rolle mehr, und statt zweimal not würd auch ein Klammernpaar um die beiden Bedingungen reichen.

P.S.: Zeigertypen werden eigentlich mit P... deklariert (also PSpringer), wenn TZeiger aber vom Lehrer kommt... :|

der olli 14. Nov 2005 20:02

Re: Name aus Liste löschen
 
So, hab noch was weiterprogrammiert und bin direkt beim nächsten Fehler angelangt: Wenn ich das erste Element in der Liste löschen will, kommt zwar keine Fehlermeldung, das Element wird aber auch nicht aus der Listbox entfernt. Wenn ich daraufhin das nachfolgende Element löschen will, ist dieses angeblich nicht mehr in der Liste.
Ähnliches, wenn ich ein einziges Element aus der Listbox löschen will, ohne, dass noch andere vorhanden sind. Wenn ich das versuche, kommt ein Fehler und das Programm stürzt ab.

Hier mal mein etwas veränderter Lösch-Algorithmus (der Rest ist gleich geblieben):
Delphi-Quellcode:
procedure TSkispringen.bt_springer_loeschenClick(Sender: TObject);
var lauf : TZeiger;
    such : String;
begin
 such := InputBox('Eingabe','Name','');
 lauf := oben;
 WHILE (lauf^.nachher <> nil) AND (such <> lauf^.name) DO
  lauf := lauf^.nachher;
 IF (lauf^.name = such) AND (lauf^.nachher = nil) AND (lauf^.vorher <> nil) THEN // letztes Element
  lauf^.vorher^.nachher := nil
 ELSE IF (such = lauf^.name) AND (lauf^.vorher <> nil) AND (lauf^.nachher <> nil) THEN // nicht erstes/letztes Element
 begin
  lauf^.vorher^.nachher := lauf^.nachher;
  lauf^.nachher^.vorher := lauf^.vorher
 end
 ELSE IF such = oben^.name THEN // erstes Element
  oben^.nachher^.vorher := nil
 ELSE
 begin
  ShowMessage('Springer nicht in der Liste!');
  exit
 end;
 ausgabe
end;
P.S.: Ich glaube, ich spreche das oberste Element falsch an, denn wenn ich nur ein einziges Element in der Liste habe und dieses löschen will, bricht das Programm ab und markiert die Zeile
Delphi-Quellcode:
oben^.nachher^.vorher := nil
!

P.S.: Das TZeiger kam tatsächlich vom Lehrer ;)

Niko 14. Nov 2005 21:07

Re: Name aus Liste löschen
 
Ich hab' deine if's mal ein bisschen entwirrt :wink:
Delphi-Quellcode:
// ...
while (lauf^.nachher <> nil) and (such <> lauf^.name) do
  lauf := lauf^.nachher;
if (lauf^.name = such) then
begin
  if (lauf = oben) then
    oben := lauf^.nachher
  else
    lauf^.vorher^.nachher := lauf^.nachher;
  if (lauf^.nachher <> nil) then
    lauf^.nachher^.vorher := lauf^.vorher;
  Dispose(lauf);
  ausgabe;
end
else
  ShowMessage('Springer nicht in der Liste!');
[edit] Free in Dispose geändert. [/edit]

marabu 14. Nov 2005 21:25

Re: Name aus Liste löschen
 
Hallo Olli,

deine Suchschleife für die doppelt verkettete Liste sieht noch etwas merkwürdig aus. Für eine solche Funktion überlegt man sich - wie stets - was geht rein, was soll raus kommen? such ist konstant, lauf ist variabel. Die Vorbedingung für lauf ist "lauf = oben", das hast du schon mal richtig gemacht. Hast du dir eigentlich jemals Gedanken über die Nachbedingung gemacht?

Delphi-Quellcode:
procedure TSkispringen.bt_springer_loeschenClick(Sender: TObject);
var
  lauf: TZeiger;
  such: String;
BEGIN
  such := InputBox('Eingabe', 'Name', '');

  // precondition: lauf = oben
  lauf := oben;
  WHILE (lauf <> nil) AND (such <> lauf^.name) DO
    lauf := lauf^.nachher;
  // postcondition:
  // lauf = nil g.d.w. nicht gefunden
  // lauf <> nil g.d.w. lauf^.name = such


  // Und auch der eigentliche Aktionsteil
  // sieht aufgeräumt aus, wenn du weißt, was du tust

  IF NichtGefunden THEN
    ShowMessage('hä?')
  ELSE
  BEGIN
    IF EsGibtEinenNachfolger THEN
      lauf^.nachher^.vorher := lauf^.vorher;

    IF EsGibtEinenVorggänger
      THEN lauf^.vorher^.nachher := lauf^.nachher
      ELSE EsGibtEinenNeuenErsten;

    // Kannst Speicher ruhig wieder freigeben
    // Brauchst ihn ja doch nicht mehr
    Dispose(lauf);
    Ausgabe();
  END;
END;
Grüße vom marabu

der olli 14. Nov 2005 21:43

Re: Name aus Liste löschen
 
Vielen Dank für eure Hilfe. Mit Nikos verbessertem Code funktioniert es (außer der Funktion free: "Zu viele Parameter", aber das liegt wohl daran, dass wir mit dem alten Delphi 4 arbeiten.
Ich habe dann die Funktion dispose benutzt, die das selbe bewirkt wie free, oder? Auf jeden fall danke für eure Hilfe!

Niko 15. Nov 2005 19:01

Re: Name aus Liste löschen
 
Zitat:

Zitat von der olli
(außer der Funktion free: "Zu viele Parameter", aber das liegt wohl daran, dass wir mit dem alten Delphi 4 arbeiten.

Nein, das liegt einfach daran, dass ich in letzter Zeit mehr mit C als mit Delphi zu tun hatte :wink: - Dispose ist die richtige Funktion.


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:22 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz