![]() |
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:
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: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;
Delphi-Quellcode:
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??).
Var
x: TVariable<Integer>; begin x := TVariable<Integer>.Create('Test', [2,3,9]); x.Value[[0,0,2]] := 16 ... Statt der Value- Property wären auch einzelne entsprechend dimensionierte Properties denkbar:
Delphi-Quellcode:
Ist möglicherweise schneller aber nicht besonders elegant. Gibts hier eine Möglichkeit einfach die "richtige" property anzulegen zb. über Generics?
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; |
AW: Mehrdimensionale Variable variabler Dimension
Bei den Generics kann man nur veränderliche Typen deklarieren, aber die Anzahl bleibt immer gleich.
|
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:) |
AW: Mehrdimensionale Variable variabler Dimension
Die Klasse sieht nun wie folgt aus:
Delphi-Quellcode:
Bis auf die Value_ property sieht das schon recht gut aus.
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; 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:
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.
Type
TEnumEntry = AnsiString; Leider ist aber TypeOf(T) = TypeOf(TEnumEntry) = TypeOf(AnsiString). Daher suche ich nun eine Möglichkeiten die beiden Varianten zu unterscheiden? |
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. |
AW: Mehrdimensionale Variable variabler Dimension
Warum nicht so?
Delphi-Quellcode:
Jetzt hast du eine beliebige Dimensoinstiefe.
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.
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; |
AW: Mehrdimensionale Variable variabler Dimension
Zitat:
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? |
AW: Mehrdimensionale Variable variabler Dimension
Das mit dem Klonen geht auch ganz simpel, erstelle dir ein Clone Methode, die dann
Delphi-Quellcode:
kopiert und die Elemente aus dem (jetzt) Array (sind ja auch nur
FValue
Delphi-Quellcode:
wiederum clont.
IVaraíable<T>
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; |
AW: Mehrdimensionale Variable variabler Dimension
na klar :wall:, danke!
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:18 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz