AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Löschen in einer "for in" Schleife

Ein Thema von dominikkv · begonnen am 14. Dez 2013 · letzter Beitrag vom 15. Dez 2013
Antwort Antwort
Seite 1 von 2  1 2      
dominikkv

Registriert seit: 30. Sep 2006
Ort: Gundelfingen
1.109 Beiträge
 
Delphi 2007 Professional
 
#1

Löschen in einer "for in" Schleife

  Alt 14. Dez 2013, 14:45
Hallo,

ich stehe öfters mal vor dem Problem, dass ich eine Liste habe, und Einträge daraus löschen möchte:
Delphi-Quellcode:
var
  Eintrag: TMeinEintrag;
  Liste: TObjectList<TMeinEintrag>;

// ...

  for Eintrag in Liste do
     begin
      if Bedingung(Eintrag) then
         begin
          Liste.Remove(Eintrag);
         end;
     end;
Das ganze kann aber zu komischen Effekten führen - zum Beispiel wird bei mir jetzt gerade ein Eintrag übersprungen. Vielleicht kommt der Iterator nicht damit zurecht, wenn der aktuelle Eintrag aus der Liste gelöscht wird.

- Könnt ihr bestätigen, dass obiges Beispiel zu Fehlern führen kann?
- Wie löscht ihr bestimmte Einträge aus einer Liste? Eine "for I := Count - 1 downto 0"-Schleife möchte ich vermeiden.

Grüße
Dominik
Dominik
Wer anderen eine Grube gräbt, hat ein Gruben-Grab-Gerät!
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.071 Beiträge
 
Delphi 12 Athens
 
#2

AW: Löschen in einer "for in" Schleife

  Alt 14. Dez 2013, 14:52
Das problem ist, daß der Standard-Enumerator nur den Index und das Ende kennt.
Er zählt einfach nur blind hoch und weiß nicht, daß du etwas löschst.

siehe TObjectList.GetEnumerator (das ist der Enumerator, welchen For-In nimmt, wenn du selbst Keinen explizit übergibst)


Die einzigen Lösungen sind da:
- kein For-In, sondern eine While- oder eine For-Downto-Schleife nutzen
- einen eigenen Enumerator nutzen
- - entweder muß der auch rückwärts laufen
- - oder er listet erst alles auf, merkt sich das und geht dann seine Liste durch
- wenn deine TList eine ToArray-Funktion hast, dann läßt du die Liste in ein Array umwandeln und läufst dann mit dem Standard-Enumerator der Arrays über die Kopie, wobei das Array von deinem Löschen auch nichts weiß und demanch alles von seiner Kopier dir presentiert.

[add]
die generiesche TObjectList<T> geht auf TList<T> und die hat ein ToArray
for Eintrag in Liste.ToArray do

Du kannst Emba auch fragen, ob die das Löschen in dem Standardenumerator berücksichtigen.
(dürfte mit zwei/drei Zeilen Quellcode möglich sein)
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.

Geändert von himitsu (14. Dez 2013 um 14:58 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke
Online

Registriert seit: 10. Jun 2003
Ort: Berlin
9.586 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: Löschen in einer "for in" Schleife

  Alt 14. Dez 2013, 14:58
Die einfachste Lösung ist die zuletzt genannte:
Delphi-Quellcode:
  for Eintrag in Liste.ToArray do
     begin
      if Bedingung(Eintrag) then
         begin
          Liste.Remove(Eintrag);
         end;
     end;
- Wie löscht ihr bestimmte Einträge aus einer Liste? Eine "for I := Count - 1 downto 0"-Schleife möchte ich vermeiden.
Was spricht denn dagegen? Das wäre ja die schnellste Variante was den erzeugten Code angeht.
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#4

AW: Löschen in einer "for in" Schleife

  Alt 14. Dez 2013, 15:53
Das problem ist, daß der Standard-Enumerator nur den Index und das Ende kennt.
Den Index? Ich dachte immer, ein Eumerator kennt gerade *keinen* Index, sondern nur ein 'Start','Next' und 'Bin ich am Ende?'. Aber, klar: Der spezielle Listen-Enumerator arbeitet natürlich mit einem Index. Aber allgemein gesehen darf ein Enumerator keinen Index kennen. Ein gutes Beispiel ist ein Dataset oder ein Dateiverzeichnis: Dort gibt es wirklich nur die drei o.g. Befehle zum Navigieren.

Da eine For-In-Enumeration in der Umsetzung allgemeingültig sein muss, würde ich mich nie darauf verlassen, wie ein Iterator umgesetzt ist. D.h.: Löschen in Iterator: Nee.

Und deshalb sollte die Remove-Implementierung in der Liste sofort (fail-fast) eine Exception werfen, wenn ein Enumerator aktiv ist. So wie ich das hier lese, scheint das bei Delphi (mal wieder) nur halbherzig, d.h. gar nicht umgesetzt worden zu sein. Oder irre ich mich?
  Mit Zitat antworten Zitat
Perlsau
(Gast)

n/a Beiträge
 
#5

AW: Löschen in einer "for in" Schleife

  Alt 14. Dez 2013, 16:40
Ein gutes Beispiel ist ein Dataset oder ein Dateiverzeichnis: Dort gibt es wirklich nur die drei o.g. Befehle zum Navigieren.
Theoretisch könnte man in einem Dataset auch mit RecNo navigieren:
Delphi-Quellcode:
Dataset.Last;
for i := 1 to Dataset.RecordCount do
begin
  DataSet.RecNo := i;
  ...
end;
Ist aber kein guter Programmierstil und birgt etliche Fallen. Deshalb: In DataSets immer nur mit Next, Prior, First und Last navigieren und beim Durchiterieren auf DataSet.Eof prüfen.
  Mit Zitat antworten Zitat
dominikkv

Registriert seit: 30. Sep 2006
Ort: Gundelfingen
1.109 Beiträge
 
Delphi 2007 Professional
 
#6

AW: Löschen in einer "for in" Schleife

  Alt 14. Dez 2013, 19:13
Hey danke für eure Antworten

Das mit dem .ToArray gefällt mir gut (und es klappt sogar ). Das erstellt zwar eine Kopie der Liste, aber das ist in diesem Fall vertretbar.

- Wie löscht ihr bestimmte Einträge aus einer Liste? Eine "for I := Count - 1 downto 0"-Schleife möchte ich vermeiden.
Was spricht denn dagegen? Das wäre ja die schnellste Variante was den erzeugten Code angeht.
Ja das ist in der Ausführung die schnellste Variante, allerdings ist Schnelligkeit nicht immer ausschlaggebend. In meinem Fall habe ich so um die 100 Einträge in der Liste, da lege ich mehr Wert auf Lesbarkeit des Codes, außerdem spare ich somit eine Variable
Dominik
Wer anderen eine Grube gräbt, hat ein Gruben-Grab-Gerät!
  Mit Zitat antworten Zitat
Mikkey

Registriert seit: 5. Aug 2013
265 Beiträge
 
#7

AW: Löschen in einer "for in" Schleife

  Alt 14. Dez 2013, 20:42
Wenn Du unbedingt das for .. in - Konstrukt verwenden willst, legst Du halt eine Löschliste an, in die Du alle zu löschenden Kandidaten übernimmst und nach vollständigem Duchlauf diese Kandidaten gezielt löschst.

In C# führt Dein Vorgehen regelmäßig zu einer Exception. Dort ist der Umweg über den Index i.A. nicht möglich, deshalb ist der obige ein häufig beschrittener Weg.
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#8

AW: Löschen in einer "for in" Schleife

  Alt 14. Dez 2013, 21:29
... Dort ist der Umweg über den Index i.A. nicht möglich, deshalb ist der obige ein häufig beschrittener Weg.
Na ja, wenn du noch unbedingt mit .NET 2 hantieren musst, ja. Aber mit .NET 3.5 oder höher geht das aber noch einfacher
Code:
foreach (var item in myEnumerable.Where(ItemIsNotValidAnymore))
{
  myEnumerable.Remove(item);
}
...
bool ItemIsNotValidAnymore(MyEnumerableElement item)
{
 ...
}
Da braucht man noch nicht einmal mehr Kommentare.
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke
Online

Registriert seit: 10. Jun 2003
Ort: Berlin
9.586 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: Löschen in einer "for in" Schleife

  Alt 14. Dez 2013, 21:34
außerdem spare ich somit eine Variable
Die Variable ist ja trotzdem da, nur halt in der zusätzlichen Enumerationsklasse.
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#10

AW: Löschen in einer "for in" Schleife

  Alt 14. Dez 2013, 22:21
Ja das ist in der Ausführung die schnellste Variante, allerdings ist Schnelligkeit nicht immer ausschlaggebend. In meinem Fall habe ich so um die 100 Einträge in der Liste, da lege ich mehr Wert auf Lesbarkeit des Codes, außerdem spare ich somit eine Variable
Ich frage mich wirklich, was daran schlecht lesbar ist:
Delphi-Quellcode:
for i:=Liste.Count-1 downto 0 do
  if Bedingung(Eintrag[i]) Then
    Liste.Delete(i);
Oder findest Du die for-Schleife etwa 'old-school'? Den Umweg über ein Array zu gehen ist ja auch nicht gerade direkt, kurz und knapp, oder? Wenn Du das lesbar machen willst, dann benutze Refactoring;
Delphi-Quellcode:
...
LoescheBestimmteElementeAusDer(Liste);
...
Beim Lesen des Codes kann man ja wohl nicht anders, als zu lesen, was dort passiert. Da muss man noch nicht einmal großartig in die Routine reinspringen, weil das ja im Kontext der o.g. Routine sekundär sein dürfte.
Delphi-Quellcode:
Procedure LoescheBestimmteElementeAusDer(Liste : TListe);
var
  i : Integer;
Begin
  for i:=Liste.Count-1 downto 0 do
    if Bedingung(Eintrag[i]) Then
      Liste.Delete(i);
End;
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:07 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