Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Mehrdimensionale Variable variabler Dimension (https://www.delphipraxis.net/182220-mehrdimensionale-variable-variabler-dimension.html)

Whookie 9. Okt 2014 22:54

Delphi-Version: XE5

Mehrdimensionale Variable variabler Dimension
 
Bin gerade dabei eine Klasse zu bauen, mit der zur Laufzeit ein Variable angelegt werden kann, die ein Array beliebiger Dimension (0-3) eines bestimmten Types enthalten kann (Boolean, Double, Integer, AnsiString, ...).

Im Prinzip bin ich bei sowas gelandet:

Delphi-Quellcode:
 
TDimArray = Array Of Integer;

TVariable<T> = Class
  private
    fDimensions: TDimArray;
    fPData: PByte;
    fMemSize: Integer;
  public
    constructor Create(AName: AnsiString; const ADimensions: TDimArray); virtual;

    property Value[Index: TDimArray]: T read GetVal write SetVal;
 End;
Was daran natürlich nicht so elegant ist, sind die Zugriffe über Value, bei denen ich mir nicht sicher bin ob sie von der Performance her so günstig sind:

Delphi-Quellcode:
Var
  x: TVariable<Integer>;
begin
  x := TVariable<Integer>.Create('Test', [2,3,9]);
  x.Value[[0,0,2]] := 16
  ...
Verwaltet werden die Daten in einem großen Speicherblock (fPData) weil keinen Weg gefunden habe eine Mehrdimensionale Variable variabler Dimension zu definieren (geht sowas überhaupt??).

Statt der Value- Property wären auch einzelne entsprechend dimensionierte Properties denkbar:

Delphi-Quellcode:
    property Value1D[Index: Integer]: T read GetValue1D write SetValue1D;
    property Value2D[x, y: Integer]: T read GetValue2D write SetValue2D;
    property Value3D[x, y, z: Integer]: T read GetValue3D write SetValue3D;
Ist möglicherweise schneller aber nicht besonders elegant. Gibts hier eine Möglichkeit einfach die "richtige" property anzulegen zb. über Generics?

himitsu 10. Okt 2014 08:38

AW: Mehrdimensionale Variable variabler Dimension
 
Bei den Generics kann man nur veränderliche Typen deklarieren, aber die Anzahl bleibt immer gleich.

mensch72 10. Okt 2014 20:14

AW: Mehrdimensionale Variable variabler Dimension
 
etwas um die Ecke gedacht realisiert man das z.B. intern als Hash mit einem Key, welcher sich eben aus den verketteten "beliebigen Dimensionen" zusammen setzt.

Nehmen wir als Beispiel das ganze als StringHash:

setzen: sh.Add(IntToStr(x)+'-'+IntToStr(y)+'-'+IntToStr(z), stringvalue)
lesen: stringvalue := sh.Get(IntToStr(x)+'-'+IntToStr(y)+'-'+IntToStr(z));

Das ist vom Speicherplatz her optimal, weil übersprungene X,Y,Z-Indexwerte keinen Speicherplatz verbrauchen und geht auch fix.

Das Ganze schön in eine Klasse gepackt und Zugriff per PropertyValue[x,y,z] und schon sieht es aus als wärs eine Array:)

Whookie 11. Okt 2014 12:00

AW: Mehrdimensionale Variable variabler Dimension
 
Die Klasse sieht nun wie folgt aus:

Delphi-Quellcode:
TVariable<T> = Class
  private type
    PVariable = ^T;
  private
    fVarSize: Integer;
    fDimensions: TDimArray;
    fPData: PByte;
    fMemSize: Integer;
  public
    constructor Create(AName: AnsiString; const ADimensions: TDimArray); virtual;

    property Value_: T read GetValue write SetValue;
    property Value[x: Integer]: T read GetValue1D write SetValue1D; default;
    property Value[x,y: Integer]: T read GetValue2D write SetValue2D; default;
    property Value[x, y, z: Integer]: T read GetValue3D write SetValue3D; default;
  End;
Bis auf die Value_ property sieht das schon recht gut aus.

Zeitmessungen habe ich auch gemacht jeweils 800000 Zugriffe auf ein 10*3 Integer Array

Version mit Value[[x,y]] : ~2400ms
Version mit Value[x,y] : ~180ms

Zusätzlich habe ich auch das mit einem TDictionary ausprobiert:
Mit IntToStr(x)+'-'+IntToStr(y): ~9200ms
Mit (x Shl 16 + y) als Index: ~3400ms


Ein Problem bleibt noch mit der Unterscheidung zwischen:
Delphi-Quellcode:
Type
  TEnumEntry = AnsiString;
und "normalen" AnsiStrings. Beides soll als Array angelegt werden können, intern aber anders gehandhabt werden. AnsiStrings sind in Wirklichkeit Strings mit fixer Länge und können direkt abgelegt werden während TEnumEntry Strings mit varibler Länge sind und im Speicher einfach als Offsets auf ihre Position dahinter abgelegt werden sollen.

Leider ist aber TypeOf(T) = TypeOf(TEnumEntry) = TypeOf(AnsiString). Daher suche ich nun eine Möglichkeiten die beiden Varianten zu unterscheiden?

Bjoerk 11. Okt 2014 12:06

AW: Mehrdimensionale Variable variabler Dimension
 
Ich finde diese Klasse einfach nur schrecklich (schon alleine wegen der Lesbarkeit der Quellcodes die die Klasse verwenden werden), aber wegen dem AnsiString, könnte man evtl. so machen?

TEnumEntry = type AnsiString;

Dann wären (für Delphi) TEnumEntry und AnsiString zwei unterschiedliche Typen.

Sir Rufo 11. Okt 2014 13:50

AW: Mehrdimensionale Variable variabler Dimension
 
Warum nicht so?
Delphi-Quellcode:
unit DimValue;

interface

uses
  System.Generics.Collections;

type
  IVariable<T> = interface
    function GetValue: T;
    procedure SetValue( const Value: T );
    property Value: T read GetValue write SetValue;
    function GetV( index: Integer ): IVariable<T>;
    procedure SetV( index: Integer; Value: IVariable<T> );
    property V[index: Integer]: IVariable<T> read GetV write SetV; default;
  end;

  TVariable<T> = class( TInterfacedObject, IVariable<T> )
  private
    FValue: T;
    FVarDict: TDictionary<Integer, IVariable<T>>;
  private
    function GetValue: T;
    procedure SetValue( const Value: T );
    function GetV( index: Integer ): IVariable<T>;
    procedure SetV( index: Integer; Value: IVariable<T> );
  private
    constructor Create;
    destructor Destroy; override;
  public
    class function Construct: IVariable<T>;
  end;

implementation

{ TVariable<T> }

class function TVariable<T>.Construct: IVariable<T>;
begin
  Result := TVariable<T>.Create;
end;

constructor TVariable<T>.Create;
begin
  inherited;
  FVarDict := TDictionary < Integer, IVariable < T >>.Create;
end;

destructor TVariable<T>.Destroy;
begin
  FVarDict.Free;
  inherited;
end;

function TVariable<T>.GetV( index: Integer ): IVariable<T>;
begin
  if not FVarDict.TryGetValue( index, Result )
  then
    begin
      Result := TVariable<T>.Construct;
      FVarDict.Add( index, Result );
    end;
end;

function TVariable<T>.GetValue: T;
begin
  Result := FValue;
end;

procedure TVariable<T>.SetV( index: Integer; Value: IVariable<T> );
begin
  FVarDict.AddOrSetValue( index, Value );
end;

procedure TVariable<T>.SetValue( const Value: T );
begin
  FValue := Value;
end;

end.
Jetzt hast du eine beliebige Dimensoinstiefe.
Delphi-Quellcode:
procedure foo;
var
  LVar : IVariable<string>;
begin
  LVar := TVariable<string>.Construct;
  LVar[0].Value := 'foo';
  LVar[0][0].Value := 'bar';
  LVar[0][0][0].Value := 'foobar';

  WriteLn( LVar[0].Value, ',', LVar[0][0].Value, ',', LVar[0][0][0].Value ); // => foo,bar,foobar
end;

Whookie 11. Okt 2014 15:15

AW: Mehrdimensionale Variable variabler Dimension
 
Zitat:

Zitat von Sir Rufo (Beitrag 1275607)
Warum nicht so?
...

Das siehst sehr elegant aus :thumb:! Vom zeitverhalten liege ich hier zwar bei ca 4000ms aber nach dem Ersetzen von TDictionary<> gegegen TArray<> bin ich auf 1800ms gekommen. Das ist zwar immer noch um den Faktor 10 langsamer würde aber die Sache mit den Strings elegant lösen.

Einziges Problem hier ist die Duplikation der Klasse. Ich hätte ein Liste mit vielen solcher Variablen (wird aus einem XML-File geladen/erstellt) und dann bei Bedarf an einen Thread übergeben. Dazu sollte die Liste bei der Übergabe geclont werden damit es zu keinen Seiteneffekten kommt.

Mit der ursprünglichen Variante wäre nur der Speicher (fPData^) zu kopieren gewesen wie das mit dieser Variante geht ist mir noch nicht ganz klar?

Sir Rufo 11. Okt 2014 16:01

AW: Mehrdimensionale Variable variabler Dimension
 
Das mit dem Klonen geht auch ganz simpel, erstelle dir ein Clone Methode, die dann
Delphi-Quellcode:
FValue
kopiert und die Elemente aus dem (jetzt) Array (sind ja auch nur
Delphi-Quellcode:
IVaraíable<T>
wiederum clont.

Hier mal auf die Schnelle mit dem Dictionary:
Delphi-Quellcode:
function TVariable<T>.Clone : IVariable<T>;
begin
  Result := TVariable<T>.Construct;
  Result.Value := FValue;
  for LKey in FVarDict.Keys do
    Result[LKey] := FVarDict[LKey].Clone;
end;

Whookie 12. Okt 2014 10:28

AW: Mehrdimensionale Variable variabler Dimension
 
na klar :wall:, danke!


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:28 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