[Erledigt] Frage zu Objektliste

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut

[Erledigt] Frage zu Objektliste

Beitragvon MacWomble » 24. Sep 2019, 09:14 [Erledigt] Frage zu Objektliste

A = Adresse aus Adressstamm
TempObj = Adresse in Akte (Aktenadresse)
Adressen = Liste der Aktenadressen


Code: Alles auswählen
procedure TAktenEditor.btnAdtresseZuweisenClick(Sender: TObject);
var
  A: TAdresse;
  TempObj: TAktenAdresse;
 
begin
  if ListeAdresse(A) then //Gibt A zurück
  begin
    TempObj := TAktenAdresse.Create; //???
 
    TempObj.ID := 0;
    TempObj.IDAdresse := A.iD;
    TempObj.IDAdressart := A.IDAdressArt;
    TempObj.Zeichen := 'Abc';
 
    If not Assigned(Adressen) Then
      Adressen:= TAktenAdressListe.Create;
 
    Adressen.Add(TempObj);
 
    //FreeAndNil(TempObj); // Wenn aktiv wird die neue Adresse in Adressen gelöscht.
 
    InitGridAktenAdresse;  // Hat Problem mit der neuen Adresse (leere Fehlermeldung)
 
  end;
end;


Die Frage ist nun, wie ich die Rückgabe direkt in die Adressen eintragen kann, ohne TempObj.
Wahrscheinlich sehe ich vor lauter Bäumen den Wald wieder nicht ... :oops:
Zuletzt geändert von MacWomble am 24. Sep 2019, 17:03, insgesamt 1-mal geändert.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.
MacWomble
Lazarusforum e. V.
 
Beiträge: 913
Registriert: 17. Apr 2008, 00:59
Wohnort: Freiburg
OS, Lazarus, FPC: Mint 19.2 Cinnamon / CodeTyphon 7.1 v. 18.02.2020/ FP 3.3.1 Rev 2002180516 | 
CPU-Target: Intel i7 64/32 Bit
Nach oben

Beitragvon six1 » 24. Sep 2019, 09:27 Re: Frage zu Objektliste

...falls ich es richtig verstanden habe, sollte es so gehen:

Code: Alles auswählen
 
If not Assigned(Adressen) Then
      Adressen:= TAktenAdressListe.Create;
setlength(Adressen, high(Adressen)+2);
Adressen[high(Adressen)].ID := 0;
Adressen[high(Adressen)].IDAdresse := A.iD;
Adressen[high(Adressen)].IDAdressart := A.IDAdressArt;
Adressen[high(Adressen)].Zeichen := 'Abc';
 
Gruß, Michael
six1
 
Beiträge: 141
Registriert: 1. Jul 2010, 18:01

Beitragvon m.fuchs » 24. Sep 2019, 09:31 Re: Frage zu Objektliste

Also: ein TAktenAdresse-Objekt musst du erzeugen, weil deine Liste das erwartet. Oder haben TAktenAdresse und TAdresse eine verwandtschaftliche Beziehung?

Du kannst aber einen Constructor schreiben, der eine TAdresse übergeben bekommt. Dann ist das ein bisschen versteckter.

Code: Alles auswählen
constructor TAktenAdresse.Create(FromData: TAdresse);
begin
  inherited Create;
  Self.ID := 0;
  Self.IDAdresse := FromData.iD;
  Self.IDAdressart := FromData.IDAdressArt;
  Self.Zeichen := 'Abc';
end;
 
(* ... *)
 
procedure TAktenEditor.btnAdtresseZuweisenClick(Sender: TObject);
var
  A: TAdresse;
begin
  if ListeAdresse(A) then begin
    if not Assigned(Adressen) then
      Adressen:= TAktenAdressListe.Create;
    Adressen.Add(TAktenAdresse.Create(A));
    InitGridAktenAdresse;  // Hat Problem mit der neuen Adresse (leere Fehlermeldung)
  end;
end;
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de
m.fuchs
Lazarusforum e. V.
 
Beiträge: 2169
Registriert: 22. Sep 2006, 18:32
Wohnort: Berlin
OS, Lazarus, FPC: Winux (Lazarus 2.0, FPC 3.0.4) | 
CPU-Target: x86, x64, arm
Nach oben

Beitragvon m.fuchs » 24. Sep 2019, 09:32 Re: Frage zu Objektliste

six1 hat geschrieben:...falls ich es richtig verstanden habe, sollte es so gehen:

Ich fürchte, du hast das falsch verstanden. Es handelt sich bei Adressen nicht um ein Array.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de
m.fuchs
Lazarusforum e. V.
 
Beiträge: 2169
Registriert: 22. Sep 2006, 18:32
Wohnort: Berlin
OS, Lazarus, FPC: Winux (Lazarus 2.0, FPC 3.0.4) | 
CPU-Target: x86, x64, arm
Nach oben

Beitragvon wp_xyz » 24. Sep 2019, 09:42 Re: Frage zu Objektliste

Ich nehme an, TAktenAdressListe stammt von TList, TObjectList o.ä. ab. Dieser kannst du eine Methode AddAdresse mit den nötigen Parametern geben und du hast so die Erzeugung der temporären TAktenAdresse von deinem Programm in die Klasse TAktenAdressListe verlagert. Komplett ohne temporäres Objekt geht es nicht.

Code: Alles auswählen
function TAktenAdressListe.AddAdresse(ID: Integer; Zeichen: string; <... etc ...>): integer;
var
  tempObj: TAktenAdresse;
begin
  tempObj := TAktenAdresse.Create;
  tempObj.ID := ID;
  tempObj.Zeichen :=Zeichen;
  ... use
  Result := Add(tempObj);
end;
 
procedure TAktenEditor.btnAdtresseZuweisenClick(Sender: TObject);
var
  A: TAdresse;
begin
  if ListeAdresse(A) then //Gibt A zurück
  begin
    If not Assigned(Adressen) Then
      Adressen:= TAktenAdressListe.Create;
    Adressen.AddAdresse, A.ID, 'Abc', <usw...>);
    InitGridAktenAdresse; 
  end;
end;
wp_xyz
 
Beiträge: 3072
Registriert: 8. Apr 2011, 08:01

Beitragvon MacWomble » 24. Sep 2019, 09:44 Re: Frage zu Objektliste

Danke, ich werde das nachher versuchen. :)
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.
MacWomble
Lazarusforum e. V.
 
Beiträge: 913
Registriert: 17. Apr 2008, 00:59
Wohnort: Freiburg
OS, Lazarus, FPC: Mint 19.2 Cinnamon / CodeTyphon 7.1 v. 18.02.2020/ FP 3.3.1 Rev 2002180516 | 
CPU-Target: Intel i7 64/32 Bit
Nach oben

Beitragvon Warf » 24. Sep 2019, 16:59 Re: Frage zu Objektliste

six1 hat geschrieben:...falls ich es richtig verstanden habe, sollte es so gehen:

Selbst wenn du es richtig verstanden hast ist das extrem unperformant, da im worst case mit jedem aufruf einmal alle addressen kopiert werden müssen. Daher sollte man falls möglich immer auf TList oder so zurückgreifen, da die geometrisches wachstum haben, und nur logarithmisch oft resizen (und damit potentiell den gesammten Array kopieren) müssen

Zum Thema:
Die Frage ist nun, wie ich die Rückgabe direkt in die Adressen eintragen kann, ohne TempObj.

Sind das records oder Klassen? Denn wenn es Klassen sind, dann scheiß drauf, dein TempObj ist nur ein pointer, d.h. nur 4 oder 8 Byte groß, und den speicher auf den es zeigt brauchst du ja eh (da ja in der liste auch nur ein Pointer steht).

Ansonsten geht auch sowas:
Code: Alles auswählen
 
if ListeAdresse(A) then //Gibt A zurück
  with Adressen[Adressen.Add(TAktenAdresse.Create)] do
  begin
    ID := 0;
    IDAdresse := A.iD;
    IDAdressart := A.IDAdressArt;
    Zeichen := 'Abc';
  end;
end;

Dann musst du nur dein
Code: Alles auswählen
 
    If not Assigned(Adressen) Then
      Adressen:= TAktenAdressListe.Create;

Irgendwo anders hin verlagern (z.B. in den Konstruktor der owner klasse, immerhin zerstörst dus wahrscheinlich ja eh im destruktor)
Warf
 
Beiträge: 1330
Registriert: 23. Sep 2014, 16:46
Wohnort: Aachen
OS, Lazarus, FPC: MacOS | Win 10 | Linux | 
CPU-Target: x86_64
Nach oben

Beitragvon MacWomble » 25. Sep 2019, 06:34 Re: [Erledigt] Frage zu Objektliste

Erneuten Dank für eure Hilfe. Gelöst habe ich es letztlich nun so - funktioniert wie es soll:

in der Form-unit
Code: Alles auswählen
procedure TAktenEditor.btnAdtresseZuweisenClick(Sender: TObject);
var
  A: TAdresse;
begin
  if ListeAdresse(A) then
  begin
    if not Assigned(Adressen) then
      Adressen := TAktenAdressListe.Create;
    Adressen.AddAdresse(A);
 
    InitGridAktenAdresse;
  end;
end;   


... und in der Klasseunit
Code: Alles auswählen
function TAktenAdressListe.AddAdresse(A: TAdresse): integer;
var
  TempObj: TAktenAdresse;
  s: string;
begin
  TempObj := TAktenAdresse.Create;
  TempObj.ID := 0;
  TempObj.IDAdresse := A.iD;
  TempObj.IDAdressart := A.IDAdressArt;
  TempObj.IDColor:=A.IDColor;
  s := A.Anrede + ' ' + A.Vorname + ' ' + A.FirmaZuname + ' ' + A.Firma2;
  TempObj.Adresszeile := s;
 
  Result := Add(TempObj);
end;                                           
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.
MacWomble
Lazarusforum e. V.
 
Beiträge: 913
Registriert: 17. Apr 2008, 00:59
Wohnort: Freiburg
OS, Lazarus, FPC: Mint 19.2 Cinnamon / CodeTyphon 7.1 v. 18.02.2020/ FP 3.3.1 Rev 2002180516 | 
CPU-Target: Intel i7 64/32 Bit
Nach oben

Beitragvon six1 » 25. Sep 2019, 08:46 Re: Frage zu Objektliste

Warf hat geschrieben:
six1 hat geschrieben:...falls ich es richtig verstanden habe, sollte es so gehen:

Selbst wenn du es richtig verstanden hast ist das extrem unperformant, da im worst case mit jedem aufruf einmal alle addressen kopiert werden müssen. Daher sollte man falls möglich immer auf TList oder so zurückgreifen, da die geometrisches wachstum haben, und nur logarithmisch oft resizen (und damit potentiell den gesammten Array kopieren) müssen


Ok, an dieser Stelle wäre eine Übergabe des Parameter als var möglich, um dies zu verhindern.
Gruß, Michael
six1
 
Beiträge: 141
Registriert: 1. Jul 2010, 18:01

Beitragvon m.fuchs » 25. Sep 2019, 08:55 Re: [Erledigt] Frage zu Objektliste

six1 hat geschrieben:
Warf hat geschrieben:
six1 hat geschrieben:...falls ich es richtig verstanden habe, sollte es so gehen:

Selbst wenn du es richtig verstanden hast ist das extrem unperformant, da im worst case mit jedem aufruf einmal alle addressen kopiert werden müssen. Daher sollte man falls möglich immer auf TList oder so zurückgreifen, da die geometrisches wachstum haben, und nur logarithmisch oft resizen (und damit potentiell den gesammten Array kopieren) müssen

Ok, an dieser Stelle wäre eine Übergabe des Parameter als var möglich, um dies zu verhindern.


Nein, das hat überhaupt nichts mit var-Parametern zu tun. Du vergrößerst dein Array da immer nur um ein Element. Wenn der Speicherplatz an der Stelle wo es liegt dafür nicht ausreicht, wird es woanders hinkopiert.
Und wenn das bei jedem Hinzufügen passiert, kann es wirklich langsam werden.
Deswegen gibt es ja diese schönen Listen, die einen Großteil der Verwaltungsarbeit bereits (und performanter) erledigen.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de
m.fuchs
Lazarusforum e. V.
 
Beiträge: 2169
Registriert: 22. Sep 2006, 18:32
Wohnort: Berlin
OS, Lazarus, FPC: Winux (Lazarus 2.0, FPC 3.0.4) | 
CPU-Target: x86, x64, arm
Nach oben

Beitragvon Warf » 25. Sep 2019, 21:45 Re: Frage zu Objektliste

six1 hat geschrieben:Ok, an dieser Stelle wäre eine Übergabe des Parameter als var möglich, um dies zu verhindern.

siehe die antwort von m.fuchs.
Als kleine erklärung wie TList (oder auch TFPGList für generische typen) das macht, die haben neben der Länger noch eine Kapazität. Wenn du eine Liste erzeugst wird eine gewisse kapazität alloziiert, z.B. 1 eintrag. Dann, solang die Länge kleiner als die Kapazität ist kann hinzugefügt werden ohne kosten. Wenn länge gleich Kapazität ist, wird die Kapazität geometrisch erhöt. Bei TList ist das glaube ich ein Faktor 2 wenn die Liste klein ist und ein Faktor 1,5 wenn die Liste größer wird.

Als array implementierung wäre das etwa so:
Code: Alles auswählen
void addItem(Item: TSomeType);
begin
  if Length = Capacity then
  begin
    Capacity = Capacity * 2;
    SetLength(Array, Capacity);
  end;
  Array[Length] := Item;
  Inc(Length);
end;

und die entsprechende RemoveItem würde dann irgendwie so aussehen (aus einfachheit nehmen wir an das nur das letzte element gelöscht wird:
Code: Alles auswählen
void removeItem();
begin
  Dec(Length)
  if Length < Capacity div 2 then
  begin
    Capacity = Capacity div 2;
    SetLength(Array, Capacity);
  end;
end;


Denn SetLength muss ja den speicherbereich vergrößern, d.h. wenn aber an der stelle wo der Speicher liegt kein freier platz hintendran mehr frei ist (Arrays garantieren ja kontinuierlichen Speicher) muss wo ganz anders neuer Speicher gesucht werden, und alle alten elemente rüberkopiert werden. Das ist verdammt teuer. Die annahme ist jetzt, kleine listen bleiben klein. daher wachsen die listen am anfang recht langsam (1-2-4-8-16-32), außerdem ist da das kopieren noch billig (32 werte zu kopieren ist nix bei ner GHZ CPU). Wenn die Liste groß wird, im extrem fall im GB bereich, willst du nicht für jedes Add ein paar gigabyte kopieren, daher muss man da sehr selten realloziieren. Außerdem kann man davon ausgehen das sobald eine Liste "warmgelaufen" ist sich ihre größe nur sehr selten ändert, d.h. es kommen wahrscheinlich ähnlich viele elemente hinzu wie gelöscht werden, die gesammt größe ändert sich also irgendwann kaum noch.
Für die Performancekitzler die sich jetzt denken: Oh mein gott im worst case verbrauche ich also doppelt so viel speicher, das ist auch kein Problem, denn auf modernen CPU's verwendet man virtuellen speicher. D.H. du kannst auf einem 64bit system problemlos 2^64 bytes alloziieren, ohne das es kracht, unabhängig wie viel speicher deine Maschine Physisch hat. Solang da nix reingeschrieben wird ist das absolut kein problem. Also selbst wenn bei einer 2 GB liste 4GB alloziiert sind, sitzen im Physischen RAM trozdem nur 2GB.
Das gilt natürlich nicht für Microcontroller! Aber sowohl bei i386, x86_64 oder auch ARM ist das praktisch kostenlos, und gibt immense performance boosts im vergleich zu jedes mal ein SetLength machen.


Aber sowas muss man als Programmierer eigentlich nicht mal wissen, wichtig ist nur, wenn man fertige Klassen hat, sollte man die auch benutzen. Die FPC und lazarusentwickler wissen (zumindest in den aller meißten fällen) was sie tun!

PS: Wenn man eine Liste von Komplexeren Objekten z.B. records hat, ist es dementsprechend auch schlauer einen Kontinuierlichen Memory block (z.B. TFPGList<RecordType>) zu verwenden, statt eine TList und pointer zu übergeben, da die TFPGList sich bereits um das alloziieren des speichers kümmert. Das new und dispose (oder auch bei Klassen Create und Free) sind relativ teure operationen, und wenn man große listen hat lohnt sich ne FPGList von nem (advanced)record oder nem object(Nicht Klasse) manchmal deutlich mehr (Memory Operationen sind mit das teuerste was man machen kann).
Warf
 
Beiträge: 1330
Registriert: 23. Sep 2014, 16:46
Wohnort: Aachen
OS, Lazarus, FPC: MacOS | Win 10 | Linux | 
CPU-Target: x86_64
Nach oben

Beitragvon six1 » 26. Sep 2019, 09:44 Re: [Erledigt] Frage zu Objektliste

Hey Danke für deine tolle Erklärung!
Werde das Thema demnächst für mich mal praktisch aufarbeiten!

Allerdings weiß ich nicht genau, ob es für mich Vorteile hat, da ich normalerweise eng mit Datenbanken zusammen arbeite und keine Kopie einer Datenbanktabelle im Speicher abbilde :D
Aber die Idee dahinter ist mir jetzt klar geworden.

Danke Warf
Gruß, Michael
six1
 
Beiträge: 141
Registriert: 1. Jul 2010, 18:01

• Themenende •

Zurück zu Freepascal



Wer ist online?

Mitglieder in diesem Forum: Google [Bot] und 3 Gäste

porpoises-institution
accuracy-worried