Elegantester Weg um eine dyamische Array zu ergänzen.

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von Mathias »

Bis jetzt sind mir 2 Methoden bekannt, um ein neues Element an eine dynamische Array zu hängen.
Iegendwie sind beide Methoden nicht elegant.

Code: Alles auswählen

  procedure AddByte1(b: byte);
  var
    len: SizeInt;
  begin
    len := Length(ByteArray);
    SetLength(ByteArray, len + 1);
    ByteArray[len] := b;
  end;

  procedure AddByte2(b: byte);
  var
    len: SizeInt;
  begin
    Insert(b, ByteArray, Length(ByteArray));
  end;
Ich habe mal versucht, es wie bei einem String zu machen, nur bricht es dies mit einen Syntax-Fehler ab.

Code: Alles auswählen

    ByteArray := ByteArray + [b]; // Syntaxfehler
    ByteArray := ByteArray + b; // Syntaxfehler
Was wieder funktioniert, ist eine totale Neuzuweisung:

Code: Alles auswählen

  ByteArray := [3, 4, 5];  // geht
Concat habe ich auch noch probiert, geht aber anscheinend nur mit Strings.

Muss man mit diesen Varianten leben, oder gibt es doch etwas eleganteres ?

PS: Ich mag mich noch erinnern, im Jahre 2018, gab es mal eine Trunc von FPC, dort wurde mit solchen Sachen experimentiert.
Da gab es mal folgenden Compileschalter, der aber unterdessen nicht mehr akzeptiert wird.

Code: Alles auswählen

{$modeswitch arrayoperators}
Im Anhang noch der Code, der sich dazumal compilieren lies.
Dateianhänge
unit1.pas
(1.54 KiB) 90-mal heruntergeladen
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von Socke »

Mathias hat geschrieben:
Fr 14. Jul 2023, 15:57
PS: Ich mag mich noch erinnern, im Jahre 2018, gab es mal eine Trunc von FPC, dort wurde mit solchen Sachen experimentiert.
Da gab es mal folgenden Compileschalter, der aber unterdessen nicht mehr akzeptiert wird.

Code: Alles auswählen

{$modeswitch arrayoperators}
Das ist jetzt in der offiziellen Dokumentation zu FPC 3.2.2 zu finden: https://www.freepascal.org/docs-html/ref/refsu48.html

Du kannst dir auch eigene TypeHelper bauen:

Code: Alles auswählen

type
TArray = array of byte;
TMyArrayHelper = type helper for TArray
  procedure Append(a: Byte);
end;

procedure TMyArrayHelper.Append(a: Byte);
begin
  SetLength(Self, Length(Self)+1);
  Self[High(Self)]:=a;
end;

var
  a: TArray;
begin
  a.Append(1);
end;
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von Mathias »

Das ist jetzt in der offiziellen Dokumentation zu FPC 3.2.2 zu finden: https://www.freepascal.org/docs-html/ref/refsu48.html
Danke, immerhin ein Teilerfolg.

Was sich dazumal noch kompilieren lies, wen man eine statische zu einer dynamischen Array addierte.
Aber es war fehlerhaft, darum kann ich mir vorstellen, das es deswegen wieder entfernt wurde.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von Warf »

Lustigerweise kannst du wenn ArrayOperators nicht aktiv ist deine eigenen Operatoren überladen:

Code: Alles auswählen

type
  TIntArray = Array of Integer;

operator +(const LHS: TIntArray; const RHS: Integer): TIntArray;
begin
  Result := LHS;
  SetLength(Result, Length(Result) + 1);
  Result[High(Result)] := RHS;
end;

var
  arr: TIntArray;
begin
  arr := [];
  arr += 1;
  arr += 2;
  arr += 3;
  WriteLn(arr[1]);
  ReadLn;
end.
Wenn ArrayOperators aber gesetzt ist geht das nicht.

Außerdem natürlich der obligatorische Hinweis: Elemente einzeln hinzuzufügen ist ziemlich lahm. Ich machs zwar auch gerne weil += [Value] einfach sehr hübsch ist, aber auch nur wenn es für wenige elemente oder nur sehr sporadisch vorkommt. Bei Hunderten bis Tausenden oder mehr einträgen wird das ziemlich schnell ziemlich Lahm, deshalb implementieren klassen wie TList so genanntes geometrisches Wachstum. Dabei wird die größe des Arrays immer um einen Geometrischen Faktor vergrößert (also z.B. *1.5 oder *2) also wächst exponentiell und somit werden nur Logarithmisch viele Resizings ausgeführt. Beispiel: https://github.com/Warfley/ObjPasUtils/ ... or.pas#L24

Code: Alles auswählen

generic function CollectArrayGeometric<T>(AIterator: specialize IIterator<T>): specialize TArray<T>;
var
  Head, len: SizeInt;
begin
  Result := nil;
  Head := 0;
  len := 2048 div SizeOf(T); // half a page
  SetLength(Result, len);
  while AIterator.MoveNext do
  begin
    if head >= len then
    begin
      len *= 2;
      SetLength(Result, len);
    end;
    Result[Head] := AIterator.Current;
    Inc(Head);
  end;
  SetLength(Result, Head);
end;

generic function CollectArrayLinear<T>(AIterator: specialize IIterator<T>): specialize TArray<T>;
begin
  Result := nil;
  while AIterator.MoveNext do
    Insert(AIterator.Current, Result, Length(Result));
end;
CollectArrayLinear ist offensichtlich deutlich einfacher von der Implementierung, und wie gesagt für nur wenige elemente vollkommen ausreichen, wenn es allerdings recht viele elemente sind, dann ist CollectArrayGeometric um ein vielfaches schneller

Gilt übrigens genauso für Strings, char-weises verlängern von Strings ist extrem ineffizient

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von Mathias »

Gilt übrigens genauso für Strings, char-weises verlängern von Strings ist extrem ineffizient
Das kann ich mir gut vorstellen, daher arbeiten auch die meisten Editoren mit StringArray oder StringListen. Da wird bei Einfügen eines Zeichen nur eine Zeile manipuliert und nicht der ganze Textblock.
Außerdem natürlich der obligatorische Hinweis: Elemente einzeln hinzuzufügen ist ziemlich lahm.
Dies ist auch nur interessante, wen man einzelne Elemente hinzufügen will.

Was auch noch zu empfehlen ist, wen es bekannt ist, wie gross die Array werden soll, von Anfang an, den ganzen Speicher mit SetLength() reservieren.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von Mathias »

Ich habe noch ein wenig mit den Array-Operatoren probiert und habe folgendes festgestellt.

Code: Alles auswählen

{$modeswitch arrayoperators}

type
  TVector2f = array[0..1] of single;
  TVector3f = array[0..2] of single;

var
  arr: array of single = nil;
  v2: TVector2f;
  v3: TVector3f;

begin
  // geht alles
  v2 := [1, 2];
  v3 := [1, 2, 3];

  arr += [v2];
  arr += [v3];

  arr := arr + [v3];
  arr := arr + arr + arr;

  // geht alles nicht
  arr := arr + [v3] + arr;
  arr := [v2, v2];
  arr := [v2] + [v3];
  arr := arr + [v2] + [v3];
  arr := [v2] + [1];
end.
Wenn alle Elemente dynamisch sind, kann man man mehrere auf einmal zusammensetzen.
Aber sobald eine statische Array dabei ist, geht es nur in Einzelschritten.

Ist dies bewusst so gemacht ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

PascalDragon
Beiträge: 834
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von PascalDragon »

Es sollten auch diese Zeilen nicht kompilieren:

Code: Alles auswählen

  arr += [v2];
  arr += [v3];

  arr := arr + [v3];
Du fügst hier nämlich ein dynamisches Array bestehend aus einem statischen Array aus 2 bzw. 3 Singles zu einem Array aus Singles hinzu. Das ist einfach nur falsch und demnach ein Bug im Compiler, der gefixt werden muss.

Was hypotetisch funktionieren könnte ist folgendes:

Code: Alles auswählen

  arr += v2;
  arr += v3;

  arr := arr + v3;
  arr := arr + arr + arr;

  // geht alles nicht
  arr := arr + v3 + arr;
  arr := v2 + v2;
  arr := v2 + v3;
  arr := arr + v2 + v3;
  arr := v2 + [1];
Allerdings ist der +-operator aktuell nur für dynamische Arrays definiert.
FPC Compiler Entwickler

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von Mathias »

Das ist einfach nur falsch und demnach ein Bug im Compiler, der gefixt werden muss.
Dann stecken diese Funktionen noch recht in den Kinderschuhen ?
Somit sollte man diese noch mit Vorsicht geniessen ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von Mathias »

Ich habe da noch was schönes zu den Array entdeckt.

Code: Alles auswählen

var
  ba: array of Integer = (1, 2, 3, 4, 5, 6, 7, 8, 9);
  bb: array of Integer = nil;

begin
  bb := Copy(ba, 2, 3);
  WriteLn(Length(bb));  // --> 3
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

wp_xyz
Beiträge: 4895
Registriert: Fr 8. Apr 2011, 09:01

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von wp_xyz »

Bei all dem Lobgesang über den + Operator bei dynamischen Arrays doch auch eine Unschönheit: Wenn dieses Feature aktiv ist ($modeswitch arrayoperators}, dann kann man den + Operator nicht mehr überladen, um ihn z.B. als Additions-Operator von Vektoren zu betrachten.

Das folgende kleine Programm addiert zwei Arrays of Integer (TIntegerDynArray) wie Vektoren. Wenn aber der arrayoperators-Modeswitch aktiv ist, verwirft der Compiler diese Syntax.

Code: Alles auswählen

program Project1;

{$mode objfpc}
//{$modeswitch arrayoperators}

uses
  SysUtils, Types;

operator + (a, b: TIntegerDynArray): TIntegerDynArray;
var
  i: Integer;
begin
  if Length(a) <> Length(b) then
    raise Exception.Create('Beide Arrays müssen dieselbe Länge haben.');
  SetLength(Result, Length(a));
  for i := 0 to High(a) do
    Result[i] := a[i] + b[i];
end;

var
  a: array of integer = (1, 2, 3);
  b: array of integer = (3, 2, 1);
  c: array of integer = nil;
  i: Integer;
begin
  c := a + b;
  for i := 0 to High(c) do
    Write(c[i], ' ');
  WriteLn;
  ReadLn;
end.

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von Mathias »

Bei all dem Lobgesang über den + Operator bei dynamischen Arrays doch auch eine Unschönheit: Wenn dieses Feature aktiv ist ($modeswitch arrayoperators}, dann kann man den + Operator nicht mehr überladen, um ihn z.B. als Additions-Operator von Vektoren zu betrachten.
Jetzt hast du mir gerade einen Schrecken eingejagt.
Aber zum Glück geht es noch mit statischen Array und die bracht man bei OpenGL noch recht viel..
Diese Beispiel funktioniert noch.

Code: Alles auswählen

type
  TVector3 = array[0..2] of integer;

  operator +(a, b: TVector3): TVector3;
  var
    i: integer;
  begin
    for i := 0 to High(a) do begin
      Result[i] := a[i] + b[i];
    end;
  end;

var
  a: TVector3 = (1, 2, 3);
  b: TVector3 = (3, 2, 1);
  c: array of integer = nil;
  c3: TVector3;
  i: integer;

begin
  c := nil;
  c := c + [b];
  c := c + [a];
  c3 := a + b;            
Aber mir ist jetzt noch etwas aufgefallen.
Du hast weiter oben geschrieben, das die [] beim zusammenhängen nicht optimal sei.
Ich vermute daher, das man die [] angeben muss, das der Compiler weis, das er die Elemente zusammenzählen muss, anstelle die Überladene Funktion zu verwenden.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

PascalDragon
Beiträge: 834
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von PascalDragon »

Mathias hat geschrieben:
Fr 29. Sep 2023, 17:27
Das ist einfach nur falsch und demnach ein Bug im Compiler, der gefixt werden muss.
Dann stecken diese Funktionen noch recht in den Kinderschuhen ?
Somit sollte man diese noch mit Vorsicht geniessen ?
Nicht mehr als dass du sämtliche andere Funktionalitäten des Compilers mit Vorsicht genießen musst. Bugs gehören nun mal dazu und auch, wenn wir Entwickler versuchen neue Features vollumfänglich abzudecken gelingt das eben nicht immer, da der Compiler ein komplexes Stück Software ist.
wp_xyz hat geschrieben:
Sa 30. Sep 2023, 17:10
Bei all dem Lobgesang über den + Operator bei dynamischen Arrays doch auch eine Unschönheit: Wenn dieses Feature aktiv ist ($modeswitch arrayoperators}, dann kann man den + Operator nicht mehr überladen, um ihn z.B. als Additions-Operator von Vektoren zu betrachten.
Ein Grundsatz von FPC ist, dass nur Operatoren überladen werden können, die nicht einen eingebauten Operator haben. Mit $modeswitch ArrayOperators wird eben ein weiterer, interner Operator hinzugefügt und meine Designentscheidung war dann eben, dass der eingebaute Operator Vorrang hat und eventuelle existierende Operator Overloads ignoriert werden.
FPC Compiler Entwickler

wp_xyz
Beiträge: 4895
Registriert: Fr 8. Apr 2011, 09:01

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von wp_xyz »

PascalDragon hat geschrieben:
Di 3. Okt 2023, 21:47
wp_xyz hat geschrieben:
Sa 30. Sep 2023, 17:10
Bei all dem Lobgesang über den + Operator bei dynamischen Arrays doch auch eine Unschönheit: Wenn dieses Feature aktiv ist ($modeswitch arrayoperators}, dann kann man den + Operator nicht mehr überladen, um ihn z.B. als Additions-Operator von Vektoren zu betrachten.
Ein Grundsatz von FPC ist, dass nur Operatoren überladen werden können, die nicht einen eingebauten Operator haben. Mit $modeswitch ArrayOperators wird eben ein weiterer, interner Operator hinzugefügt und meine Designentscheidung war dann eben, dass der eingebaute Operator Vorrang hat und eventuelle existierende Operator Overloads ignoriert werden.
Naja, man kann damit leben, man muss es halt wissen. Ansonsten hätte ich mich für die Array-Verkettung nicht an dem "&" der BASIC-Strings gestört, und das Thema wäre vom Tisch gewesen.

PascalDragon
Beiträge: 834
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von PascalDragon »

wp_xyz hat geschrieben:
Di 3. Okt 2023, 21:56
PascalDragon hat geschrieben:
Di 3. Okt 2023, 21:47
wp_xyz hat geschrieben:
Sa 30. Sep 2023, 17:10
Bei all dem Lobgesang über den + Operator bei dynamischen Arrays doch auch eine Unschönheit: Wenn dieses Feature aktiv ist ($modeswitch arrayoperators}, dann kann man den + Operator nicht mehr überladen, um ihn z.B. als Additions-Operator von Vektoren zu betrachten.
Ein Grundsatz von FPC ist, dass nur Operatoren überladen werden können, die nicht einen eingebauten Operator haben. Mit $modeswitch ArrayOperators wird eben ein weiterer, interner Operator hinzugefügt und meine Designentscheidung war dann eben, dass der eingebaute Operator Vorrang hat und eventuelle existierende Operator Overloads ignoriert werden.
Naja, man kann damit leben, man muss es halt wissen. Ansonsten hätte ich mich für die Array-Verkettung nicht an dem "&" der BASIC-Strings gestört, und das Thema wäre vom Tisch gewesen.
Das wäre 1. nicht Delphi-kompatibel gewesen (allein deswegen wurde es eingeführt) und 2. hat & schon mehrere Bedeutungen in Pascal (Oktalzahl und Escape für Keywords).
FPC Compiler Entwickler

Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Elegantester Weg um eine dyamische Array zu ergänzen.

Beitrag von Mathias »

Code: Alles auswählen

Mit einem kleinen Umweg ist es auch verschachtelt möglich.
Man muss einfach eine Typenumwandlung einfügen.

Code: Alles auswählen

type
  TVector3f = array[0..2] of GLfloat;
  Tmat3x3 = array[0..2] of TVector3f;
  SphereVertex: array of Tmat3x3;

 ...
  procedure Triangles(Vector0, Vector1, Vector2: TVector3f);
  var
    m3: Tmat3x3;
  begin
    // alt
    m3 := [Vector0, Vector1, Vector2];
    SphereVertex += [m3];

    // neu
    SphereVertex += [Tmat3x3([Vector0, Vector1, Vector2])];    
Direkt geht es nicht.

Code: Alles auswählen

    SphereVertex += [[Vector0, Vector1, Vector2]];  // Fehler
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten