Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Grundlegende Frage zur Strukturierung von Records und tList (https://www.delphipraxis.net/128615-grundlegende-frage-zur-strukturierung-von-records-und-tlist.html)

BoolString 2. Feb 2009 22:06


Grundlegende Frage zur Strukturierung von Records und tList
 
Schönen guten Abend,

ich war früher viel in einem anderen Forum unterwegs, welches aber leider geschlossen wurde. Aus dem Grund mache ich jetzt meine ersten Versuche hier auf Delphi-Praxis. Mögliche Fehlbedienungen möge man mir nachsehen ;-)

Ich habe ein seit längerem andauerndes Projekt, bei dem ich eine Anzahl von Strings verwalte. Jedes Zeichen eines Strings steht für Objekte (Amino- oder Nukleinsäuren) und ich führe darauf Wiederholungs- und Ähnlichkeitsanalysen durch. Jeder String ist ein Teil eines Records und trägt zusätzlich eine Feld für einen Namen. Die Strings verwalte ich in einer tList. Die grundlegende Idee habe ich im folgenden einmal skizziert:

Delphi-Quellcode:
Type
  pOneSequence = ^tSequence;

  tSequence = Record
                Name      : String;
                Data      : String;
              end;

tSequenceList = class(TObject)
  Private
    fSequenceList : tList;

  Public
    Constructor Create;
    Destructor Destroy;
    Function  NewSequence : Integer;
    Function  SortListAscendingByName : Boolean;
    // [...]
  end;

Implementation
Constructor tSequenceList.Create;
Begin
  Inherited Create;
  fSequenceList := TList.Create;
end;



Destructor tSequenceList.Destroy;
Var i      : Integer;
    aRecord : pOneSequence;
Begin
 For i := fSequenceList.Count - 1 downto 0 do
 Begin
   aRecord := fSequenceList.Items[i];
   Dispose (aRecord);
   fSequenceList.Delete (i);
 end;

 Inherited Destroy;
end;

Function CompareNamesAscending(Item1, Item2: Pointer): Integer;
Begin
  Result := AnsiStrIComp (PChar (pOneSequence(Item1)^.Name), PChar (pOneSequence (Item2)^.Name));
end;


Function tSequenceList.SortListAscendingByName : Boolean;
Begin
  Try
    fSequenceList.Sort (@CompareNamesAscending);
    Result := True;
  Except
    Result := False;
  end;
end;

Durch einige Weiterentwicklungen würde ich ein ähnliches System nun gerne für Zahlen und Matritzen anwenden. Dazu ist meine Überlegung, daß ich den Datentyp tSequence erweitere. Leider geht natürlich so etwas nicht:

Delphi-Quellcode:
 tSequenceType = (seqNumericList = 0,
                  seqNumericArray = 1,
                  seqString      = 2);

 t1dVariantArrayDouble= Record
                            Title    : String;
                            RowTitles : Array of String;                          
                            Cells    : Array of Double;
                          end;

 t2dVariantArrayDouble= Record
                            Title    : String;
                            RowTitles : Array of String;
                            ColTitles : Array of String;
                            Cells    : Array of Array of Double;
                          end;

 tSequence = Record
                Name        : String;
                Case SeqType : tSequenceType of
                   seqNumericList : (Data : t1dVariantArrayDouble);
                   seqNumericArray : (Data : t2dVariantArrayDouble);
                   seqText        : (Data : String);
                end;
Als einfachstes Rumpfuschen bestünde nun die Option einen klassischen Record zu verwenden und dort alle offenen Arrays drin zu implementieren. Die Teile die genutzt werden würden gesetzt, der Rest auf die Länge Null minimiert. Letztlich geht es mir um die Frage des Datendesigns und wie man es am sinnigsten implemetieren kann.

Es ist mir klar, daß ich viel ändern muss, wenn ich zukünftig auch solch unterschiedlichen Elemente bearbeiten will, aber die Frage ist nun wie geht man da am Besten ran. Hierzu bin ich nun brennend dran interessiert, was andere Leute für Lösungsansätze wählen würden.

Da die Funktionalität des Programms in der letzten Zeit deutlich an Umfang zugenommen hat, ist damit zu rechnen, daß später evtl. noch andere Datentypen in der Liste untergebracht werden können. Jetzt ist noch so einer der letzten Punkte, daß ich 'Altlasten' ausbügeln kann.

Momentan erscheint mir am sinnigsten, daß man hier evtl. zwei Klassen aufbaut. Die eine, die Daten verwaltet, die andere die Objekte der ersten Klasse verwaltet. Oder hat noch jemand andere Ideen? Falls ja, wie würde sich das dann äußern, bzw. wie würde es dann aussehen. Ich bin momentan in der verzwickten Lage, das ich eines dieser als 'Quick'n'dirty' geplanten Programme versuche auf eine evtl. zukunftsweisende Richtung umzustellen (auch wenn es bisher nur umsonst für einen kleinen Nutzerkreis zur Verfügung gestellt ist...)

Liebe Grüße, bin gespannt auf Ideen und Umsetzungsvorschläge...

BoolString;

sirius 2. Feb 2009 22:54

Re: Grundlegende Frage zur Strukturierung von Records und tL
 
Also abgesehen davon, dass du auch Klassen verwenden könntest anstatt den Records, wo liegt jetzt dein Problem? An welcher Stelle möchtest du wie die Zugriffe auf die Felder vereinfachen?

himitsu 2. Feb 2009 23:10

Re: Grundlegende Frage zur Strukturierung von Records und tL
 
Klassen währen wohl gut.

und sowas geht eh nich,
Delphi-Quellcode:
Case SeqType : tSequenceType of
  seqNumericList : (Data : t1dVariantArrayDouble);
  seqNumericArray : (Data : t2dVariantArrayDouble);
  seqText        : (Data : String);
end;
da mann dynamische Arrays und Strings nicht in varianten Recordteilen verwenden darf/kann ... da hat Delphi was dagegen, wegen nich eindeutiger Speicherverwaltung.

BoolString 3. Feb 2009 08:57

Re: Grundlegende Frage zur Strukturierung von Records und tL
 
Hallo Sirius,

mein Problem liegt eigentlich eher darin, daß ich am Überlegen bin, was das sinnvollste ist. Es gibt hierbei ja durchaus die eine oder andere Option, wie man an eine Lösung herangeht. Einige der Lösungen sind allerdings für mögliche spätere Erweiterungen nicht unbedingt die Besten. Es geht also weniger um ein vereinfachen von Zugriffen, sondern vielmehr um all
Meine Stärke liegt eher in der Entwickelung der Algorithmen/Berechnungen als im Klassendesign. Aus dem Grund würde mich interessieren, wie andere hier herangehen und was deren Meinung nach Vor- und Nachteile unterschiedlicher Ansätze sind...

@himitsu:
Ja, in varianten Teilen eines Records kann man keine varianten Arrays unterbringen. In klassischen Records ginge das schon. Mir fehlt bisher die Vorstellung, wie man am Besten eine entsprechende Klasse aufbaut, die die einzelnen Datensätze kapselt. Ebenso wie diese dann in einer tList am Besten verwaltet wird und man sinnvoll über eine Schnittstelle zugängig macht.

Hoffe ich hab mich nicht zu konfus ausgedrückt...

BoolString;

himitsu 3. Feb 2009 09:58

Re: Grundlegende Frage zur Strukturierung von Records und tL
 
du kannst die ja eine Basisklasse mit den nötigen (grundlegenden) Schnittstellen erstellen und von dieser dann jeweils eine Klasse für NumericList, NumericArray und String davon ableiten.

sirius 3. Feb 2009 10:08

Re: Grundlegende Frage zur Strukturierung von Records und tL
 
Und dann kannst du auch eine TObjectList anstatt TList nehmen.

BoolString 4. Feb 2009 15:39

Re: Grundlegende Frage zur Strukturierung von Records und tL
 
Besten Dank für die bisherigen Anregungen!

Ich habe mich gestern Abend einmal hingesetzt und versucht etwas entsprechendes aufzubauen. Dazu sind nun einzelne Klassen für unterschiedliche Datenobjekte entstanden. Nach der Anregung von Sirius habe ich eine ObjectList zur Hälterung der einzelnen Datenobjekte verwendet. Leider muss ich aber feststellen, daß meine Erfahrungen im Klassendesign mangelhaft sind und ich somit an einigen Punkten nicht weiterkomme. Hier ein Ausschnitt aus meinem Versuch, der die relevanten Teile beinhaltet:

Delphi-Quellcode:
Type
  tDataSetType = (seqString   = 0,
                  seqValueList = 1,
                  seqMatrix   = 2,
                  seqTable    = 3 );


  tDataTypeName     = String;
  tDataTypeString   = String;
  tDataTypeValueList = Array of Double;


  t2dVariantArrayDouble = Record
                            Title    : String;
                            RowTitles : Array of String;
                            ColTitles : Array of String;
                            Cells    : Array of Array of Double;
                          end;



  tDataObjectString = Class (tObject)
  Private
    fName : tDataTypeName;
    fData : tDataTypeString;
    Function CountDataChars : Longint;
  Public
    Property Name : tDataTypeString read fName write fName;
    Property Data : tDataTypeString read fData write fData;
    Property Count : Longint read CountDataChars;
  end;



  tDataObjectMatrix = Class (tObject)
  Private
    fName : tDataTypeName;
    fData : t2dVariantArrayDouble;
    Function CountDataRows : Longint;
    Function CountDataCols : Longint;
  Public
    Property Name : tDataTypeString read fName write fName;
    Property Data : t2dVariantArrayDouble read fData write fData;
    Property Rows : Longint read CountDataRows;
    Property Cols : Longint read CountDataCols;
  end;



  tDatasets = Class (tObject)
  Private
    fDatasetList : tObjectList;
  Public
    Constructor Create;
    Destructor Destroy;
    Function   New (SequenceType : tDatasetType) : Integer;
    Function   Get (aIndex: Longint; Var aDataObject: tDataObjectString) : Boolean; overload;
    Function   Get (aIndex: Longint; Var aDataObject: tDataObjectValues) : Boolean; overload;
    Function   Get (aIndex: Longint; Var aDataObject: tDataObjectMatrix) : Boolean; overload;
  end;



Implementation
// --------------------------------- Datenobjekt tDataObjectString
Function tDataObjectString.CountDataChars : Longint;
begin
  Result := Length (fData);
end;


// --------------------------------- Datenobjekt tDataObjectMatrix

Function tDataObjectMatrix.CountDataRows : Longint;
Begin
  Result := High(fData.Cells) + 1;
end;

Function tDataObjectMatrix.CountDataCols : Longint;
Begin
  if CountDataRows > 0 THen Result := High(fData.Cells [0]) + 1
                       Else Result := 0;
end;


// --------------------------------- Liste der Datenobjekte
Constructor tDatasets.Create;
Begin
  Inherited Create;
  fDatasetList := tObjectList.Create;
  fDatasetList.OwnsObjects := True;
end;


Destructor tDatasets.Destroy;
Var aObject : Integer;
Begin
 For aObject := fDatasetList.Count - 1 downto 0 do
 Begin
   fDatasetList.Delete (aObject);
 end;

 Inherited Destroy;
end;


Function tDatasets.New (SequenceType : tDatasetType) : Integer;
Begin
  Try
    Case SequenceType of
      seqString   : fDatasetList.Add (tDataObjectString.Create);
      seqValueList : fDatasetList.Add (tDataObjectValues.Create);
      seqMatrix   : fDatasetList.Add (tDataObjectMatrix.Create);
    end;
    Result := fDatasetList.Count;
  Except
    Result := -1;
  end;
end;


Function tDatasets.Get (aIndex: Longint; Var aDataObject: tDataObjectString) : Boolean;
Begin
  Result := False;

  If (aIndex >=0) And (aIndex < fDatasetList.Count) THen
  Begin
    If (fDatasetList.Items [aIndex] is tDataObjectString) THen
    Begin
      aDataObject := (fDatasetList.Items [aIndex] as tDataObjectString);
      Result := True;
    end;
  end;
end;


Function tDatasets.Get (aIndex: Longint; Var aDataObject: tDataObjectMatrix) : Boolean;
Begin
  Result := False;

  If (aIndex >=0) And (aIndex < fDatasetList.Count) THen
  Begin
    If (fDatasetList.Items [aIndex] is tDataObjectMatrix) THen
    Begin
      aDataObject := (fDatasetList.Items [aIndex] as tDataObjectMatrix);
      Result := True;
    end;
  end;
end;


Somit kann ich auch schön Datensätze erzeugen und diese per Index in der Klasse tDatasets referenzieren. Da ich nun aber variante Arrays benötige, habe ich ein Problem mit dem Zugriff. Ich habe allerdings die Vermutung, daß die Probleme meinem derzeitigen Erfahrungsschatz zuzuordnen sind.



Delphi-Quellcode:
Var DatasetList : tDatasets;
    anObject   : tDataObjectMatrix;
 
Begin
  DatasetList := tDatasets.Create;
  DatasetList.New (seqMatrix);

  If DatasetList.Get (0, anObject) THen
  Begin
    anObject.Name      := 'Testobjekt';
    anObject.Data.Title := 'Darin enthaltene Matrix';            // Der linken Seite kann nichts zugewiesen werden
    SetLength ((anObject as tDataObjectMatrix).Data.Cells, 2, 2); // Konstantenobjekt kann nicht als Var-Parameter weitergegeben werden
  end;
end;

Wie müsste man die Klassen sauber aufbauen, damit ich mit den varianten Elementen arbeiten kann?

Auch wenn es natürlich viel Material ist und sich vielleicht nicht gerade als Einstiegstopic in ein neues Forum eignet, wäre ich über konkrete Tipps dankbar ;-) Es geht mir nicht darum, daß ich hier etwas fertiges haben möchte. Aber momentan wäre ein Schubser in die richtige Richtung sehr hilfreich. Bisher habe ich überwiegend nur fertige Klassen ein wenig erweitert. Ich denke, ich muss mich da jetzt tatsächlich endlich mal reinfummeln.

Liebe Grüße und besten Dank

Jan


PS: Falls der Hintergrund interessiert: ich würde gerne ein kleines Softwareprojekt aufbohren und um Berechnungen für numerische Daten erweitern;
Serolis-Projekt

[...]


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