Generics mit Dynamischen Strukruren

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1629
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Generics mit Dynamischen Strukruren

Beitrag von corpsman »

Hallo miteinander.

Ich habe eine Generische Klasse geschrieben, welche Daten in einem Array speichert und diese wieder ausgeben kann. Im Gegebenen Beispiel wird jedes Neue element an den Anfang des Array's gestellt und alle Alten entsprechend um eins nach hinten geschoben.

Mein Komkretes Problem dreht sich nun um die Folgenden paar Zeilen :

Code: Alles auswählen

Procedure TTest.Add(V: T);
Var
  i: integer;
Begin
  setlength(fa, length(fa) + 1);
  { // Funktioniert nur mit T's die keine Pointer beinhalten
  move(fa[0], fa[1], high(fa) * sizeof(t));
  // }
  //{ // Funktioniert mit allen 
  For i := high(fa) Downto 1 Do Begin
    fa[i] := fa[i - 1];
  End;
  // }
  fa[0] := v;
End;
Ist T vom Typ String gehts nicht
Ist T ein integer gehts.

Wo ist mein Gedanklicher Fehler ?

Im Anhang das komplette Projekt zum schnellen Testen..
Dateianhänge
Kompletter_Source.zip
Projekt zum Testen
(2.66 KiB) 70-mal heruntergeladen
--
Just try it

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2822
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Generics mit Dynamischen Strukruren

Beitrag von m.fuchs »

Also bei mir geht dein Projekt komplett. Auch der Stringteil.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1629
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Generics mit Dynamischen Strukruren

Beitrag von corpsman »

Auch wenn du die betreffende Codezeile so realisiertst ?

Code: Alles auswählen

 
Procedure TTest.Add(V: T);
Var
  i: integer;
Begin
  setlength(fa, length(fa) + 1);
  //{ // Funktioniert nur mit T's die keine Pointer beinhalten
  move(fa[0], fa[1], high(fa) * sizeof(t));
  // }
  { // Funktioniert mit allen
  For i := high(fa) Downto 1 Do Begin
    fa[i] := fa[i - 1];
  End;
  // }
  fa[0] := v;
End;
 
--
Just try it

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2822
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Generics mit Dynamischen Strukruren

Beitrag von m.fuchs »

Nein, da hast du ja richtig erkannt, dass es mit Pointern nicht geht.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1629
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Generics mit Dynamischen Strukruren

Beitrag von corpsman »

Naja, der Button unten Links ist mit Pointern, und der Funktioniert. Eigentlich würde ich z.B. bei Strings davon ausgehen, das das im Prinzip doch auch nur Pointer sind, ..

Aktuell habe ich natürlich die Schleifenvariante am laufen, das Move ererscheint mir aber irgendwie "schneller" und würde es daher schon ganz gern einbauen..
--
Just try it

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2822
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Generics mit Dynamischen Strukruren

Beitrag von m.fuchs »

Das liegt wohl daran, dass die String sich etwas anders verhalten, wegen der Referenzzählung. Ich bin mir jetzt nicht sicher und rate leider nur ein wenig.

Probier es mal so, damit die Referenzzählung nicht durcheinander kommt:

Code: Alles auswählen

Procedure TTest.Add(V: T);
Var
  i: integer;
Begin
  setlength(fa, length(fa) + 1);
  Finalize(fa[High(fa)]);
   // Funktioniert nur mit Integern
  move(fa[0], fa[1], high(fa) * sizeof(t));
  Initialize(fa[0]);
  //
  //{ // Funktioniert mit allen 3en
{  For i := high(fa) Downto 1 Do Begin
    fa[i] := fa[i - 1];
  End;}
  // }
  fa[0] := v;
End;                 
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2822
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Generics mit Dynamischen Strukruren

Beitrag von m.fuchs »

Das Problem ist ja dass Move nicht tatsächlich verschiebt, also das erste Element mit einem Nullwert auffüllt. Damit bleibt die Referenz zweimal bestehen. Daher dann auch das Initialize damit dort wieder 0x0 steht.

Vielleicht kann das jemand noch besser erklären? Bei den Speicheroperationen dreht sich mir immer der Kopf ein wenig.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1629
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Generics mit Dynamischen Strukruren

Beitrag von corpsman »

Dene Erklärung macht Sinn und ist Nachvollziehbar.

In dem Sample geht das nun, leider in meiner Eigentlichen Anwendung nicht, dort sind es 2 Dimensionale Array's mit Records.

Ich schau mal wie ich das in das Sample rein bastle.
--
Just try it

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1629
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Generics mit Dynamischen Strukruren

Beitrag von corpsman »

Sodale, mach mal neun neuen Button6 und dann füge folgenden Code ein :

Code: Alles auswählen

 
 
Procedure TForm1.Button6Click(Sender: TObject);
Type
  TDataItem = Record
    Value: integer; // Der Ermittelte Wert
    Pencils: Array Of boolean; // Noch mögliche Elemente der Grundmenge, dabei entspriucht der Index dem Element und der Wert gibt an ob der wert noch möglich ist.
  End;
 
  TDataField = Array Of Array Of TDataItem; // Das Spielfeld als Globale variable => das könnte man auch besser realisieren..
 
  tt = specialize TTest < TDataField > ;
Var
  tr: TDataField;
  t: tt;
  i, j, k: Integer;
Begin
  t := tt.create;
  For i := 0 To 10 Do Begin
    setlength(tr, 3, 3);
    For j := 0 To 2 Do
      For k := 0 To 2 Do Begin
        tr[j, k].value := i;
        setlength(tr[j, k].pencils, 3);
      End;
    t.add(tr);
  End;
  For i := 0 To 10 Do Begin
    tr := t.get;
    For j := 0 To 2 Do
      For k := 0 To 2 Do Begin
        If tr[j, k].value <> i Then Begin // Hier schepperts nun weil tr in der 2. Dimension undefiniert ist.
          Raise exception.create('Fehler');
        End;
      End;
  End;
End;    
 
--
Just try it

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1629
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Generics mit Dynamischen Strukruren

Beitrag von corpsman »

So nu habe ich die Lösung, Ich kopiere in meinem Programm mehrere Einträge des Array's also muss ich auch mehrere Löschen :

Code: Alles auswählen

 
 // Neu
      move(fbuffer[0], fbuffer[olength], (fhead + 1) * sizeof(t));
      FillChar(fbuffer[0], (fhead + 1) * sizeof(t), 0);
 
// Alt
      For i := 0 To (fhead) Do Begin
        fbuffer[olength + i] := fbuffer[i];
      End;
 
Fillchar macht hier, was bei deinem Beispiel das Initialize macht. Welche der beiden Varianten nun schneller ist, ich weis es nicht ..
--
Just try it

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2822
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Generics mit Dynamischen Strukruren

Beitrag von m.fuchs »

Mal eine andere Frage: Warum machst du aus deinen Records keine Objekte und benutzt die TObjectQueue?
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1629
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Generics mit Dynamischen Strukruren

Beitrag von corpsman »

> Mal eine andere Frage: Warum machst du aus deinen Records keine Objekte und benutzt die TObjectQueue?
Keine Ahnung,
Ich finde OOP auch geil, aber ich wüsste spontan gar nicht wie ich TDataField als Object Darstellen kann.
Rein aus dem Bauch heraus würde ich sagen, dass ich ein wenig Performance verliere, wenn der die Objekte immer anlegen muss, das Ganze ist teil eines Brute Force Algorithmus => zig tausend Elemente rein und raus.
Und die Generics sind hier eigentlich ne Feine Sache. Da ich m eine Generische Klasse auch noch in ettlichen Anderen Projekten Einsetze hatte diese Diskussion auf jeden zur Folge, dass einige meiner anderen Projekte auch Fehlerfreier wurden ;)
--
Just try it

Warf
Beiträge: 2138
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Generics mit Dynamischen Strukruren

Beitrag von Warf »

Erstmal dein Problem wie bereits erklärt liegt an der referenzzählung. Das Move kopiert die kompletten Daten eins nach oben, damit stehen in [0] und in [1] das selbe Element, aber Move erhöht nicht die Referenzzählung. Dann wird [0] neu beschieben -> Referenzzähler wird dekrementiert, fällt auf 0, und es kracht.

Aber warum kopierst du die elemente überhaupt? Speicher kopieren ist die ineffizienteste Operation die du durchführen kannst (abgesehen von zugriff auf externe Geräte wie festplatte). Verwende am besten eine verkettete Liste, diese ist für Queues am effizientesten.

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1629
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Generics mit Dynamischen Strukruren

Beitrag von corpsman »

Hallo Warf,

für das hier gezeigte Beispiel hast du vollkommen Recht. Ich habe die eigentliche Implementierung extrem Reduziert, damit es hier diskutiert werden kann und der Fehler leichter Ersichtlich ist.

Wie ihr alle Richtig erkannt habt, handelt es sich bei meiner Implementierung um eine Fifo, meine Implementierung arbeitet dabei auf einem Array, welches einen Ringpuffer realisiert. Das Array muss nur erweitert werden, wenn dieses "Voll" wird. Dann allerdings wächst es Exponentiell => Der Diskutierte Fall tritt sehr selten auf, bzw über die Laufzeit immer seltener. Wenn ich mich recht erinnere machen fast alle LCL Componenten das auch so.
--
Just try it

marcov
Beiträge: 1102
Registriert: Di 5. Aug 2008, 09:37
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk)
CPU-Target: 32/64,PPC(+64), ARM
Wohnort: Eindhoven (Niederlande)

Re: Generics mit Dynamischen Strukruren

Beitrag von marcov »

Array basierte Generics müssen oft Elemente aus Arrays entfernen, und die übrige Elementen nach unten schieben.

Referenz Zähler kann man intakt halten durch das entfernte Element erst auf null zu setzen. (zb mit finalize()), und das letzte (jetzt Doppelte) Element kann man einfach mit fillchar löschen. Ein neu, auf nicht typierter Art kreiertes Element (zb mit getmem) kann man mit Initialize() initialisieren.

Die kann man in die RTL Quellen nachsehen (FPC 3.1.1 aka trunk hat mit Delphi kompatiblergenerics.* units noch etwas mehr Beispielen)

Mein eigene Generics Versuch ist hier

Antworten