Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Synonyme Bezeichnungen für eine Objekt-Eigenschaft (https://www.delphipraxis.net/74066-synonyme-bezeichnungen-fuer-eine-objekt-eigenschaft.html)

Gausi 27. Jul 2006 16:31


Synonyme Bezeichnungen für eine Objekt-Eigenschaft
 
Ich habe eine Klasse TAudioFile mit verschiedenen Eigenschaften, z.B. Artist, Titel, Album etc. (für diese Problemstellung hier alles nur Strings).

Jetzt stellt sich mir das Problem, dass ich eine Liste von solchen Objekten sortieren möchte, und zwar nicht nur nach einem Sortierkriterium, sondern nach mehreren, die der User zur Laufzeit festlegen kann. Also z.B. primäres Sortierkriterium: Artist. Bei Gleichheit: Sortierung nach Album, anschließend Sortierung nach Titel.

Wenn ich 10 Eigenschaften habe, müsste ich also 10*9 = 90 Compare-Funktionen für die Sort-Routine schreiben :shock:, wenn ich zwei "Stufen" zulasse. Bei 3 Stufen (wie im Beispiel) 10*9*8 = 720.... :pale:

Im DF ist die Idee aufgekommen, das damit zu lösen, alle Eigenschaften des Objektes in einem Array of String zu speichern, und in einer Comparefunktion auf ein Sortier-Array zurückzugreifen, in dem die Indizes der Eigenschaften gespeichert sind, nach denen primär, sekundär, etc. sortiert wird.
Ist sicherlich ne schöne Sache, nur würde diese Lösung bedeuten, dass ich in einem recht umfangreichen Projekt alle ".Artist", ".Album" etc. durch ein ".EigenschaftenArray[CONST_ARTIST]" etc. ersetzen müsste (mit entsprechenden Konstanten CONST_ARTIST). Ein zusätzlicher großer Nachteil wäre auch, dass man Dinge wie Autovervollständigung in der IDE zum schnelleren Finden der richtigen Eigenschaftsbezeichnung vergessen kann - der Code verliert an Übersichtlichkeit und Wartbarkeit.

Ich suche also eine Möglichkeit, wie ich die bisherige Struktur beibehalten kann, und sie um ein Array zu erweitern, sodass aber in den einzelnen Zellen des Arrays dieselben Infos stecken wie in den einzelnen Eigenschaften. Also so, dass ich
Delphi-Quellcode:
MyAudioFile.Artist
im Code völlig synonym zu
Delphi-Quellcode:
MyAudioFile.EigenschaftenArray[CONST_ARTIST]
verwenden kann. Dabei muss als Index auch eine Variable entsprechenden Wertes möglich sein - z.B.:
Delphi-Quellcode:
MyAudioFile.EigenschaftenArray[SortierPrioritätenArray[1]]
Eine simple Ersetzung vor dem Kompilieren ist also nicht möglich.

Flocke 27. Jul 2006 16:37

Re: Synonyme Bezeichnungen für eine Objekt-Eigenschaft
 
Dazu bietet Delphi die Möglichkeit der Indizierung von Eigenschaften.

Beispiel:
Delphi-Quellcode:
type
  TMyClassStringIndex = (siName, siAlbum, siTitel);

  TMyClass = class(TObject)
  private
    FStrings: array [TMyClassStringIndex] of string;
    function GetString(Index: TMyClassStringIndex): string;
    procedure SetString(Index: TMyClassStringIndex; const Value: string);
  protected
    property Strings[Index: TMyClassStringIndex] read GetString write SetString;
  public
    property Name: string Index siName read GetString write SetString;
    property Album: string Index siAlbum read GetString write SetString;
    // usw...
  end;
Dann kannst du intern auf ein Array zugreifen.

DGL-luke 27. Jul 2006 16:37

Re: Synonyme Bezeichnungen für eine Objekt-Eigenschaft
 
Delphi-Quellcode:
property Artist: string read Props[cARTIST] write Props[cARTIST];
falls er da meckert musst du dir halt getter und setter bauen.

Delphi-Quellcode:
public
  procedure SetArtist(value: string);
  function GetArtist: string;
published
  property Artist: string read GetArtist write SetArtist;
//Ja, genau so.... :roll:

Der_Unwissende 27. Jul 2006 17:15

Re: Synonyme Bezeichnungen für eine Objekt-Eigenschaft
 
Hi,
warum genau verwirfst du denn die Idee mit dem mehrfachen Sortieren? Ich denke die ist gar nicht so der schlechte Ansatz. Du brauchst natürlich weiterhin verschiedene Compare-Methoden (je nach Datentyp), aber für dieses Beispiel wolltest du dich ja auf Strings einschränken.
Also wenn du eine Liste von Objekten nach dem ersten Kriterium sortierst, schaffst du dies in O(n*log(n)) (im Mittel falls du Quicksort verwendest).
Gehst du diese Liste durch, benötigst du dafür O(n) Zeit, was durch das Sortieren dominiert wird. Suchst du nun hier die Gruppen, die gleich sind und sortierst diese erneut, hättest du maximal O(n*log(n)). Wenn du also nach der Effizienz gehst, ist mehrfaches Sortieren assymptotisch nicht Rechenzeitaufwändiger als einfaches Sortieren.

Allerdings kannst du hier noch einiges verbessern. Eine einfache Möglichkeit ist es, dass du Elemente, die den gleichen Wert (nach dem aktuellen Suchkriterium) besitzen in ein Array packst. Genau genommen kannst du sogar in-place arbeiten. Du hast dein zu sortierendes Array und speicherst einfach den Index der Teilfelder die nach dem aktuellen Kriterium gleiche Elemente enthalten.
Nun ist dein Feld bis auf diese Elemente schon sortiert. Das heißt, du brauchst nach dem nächsten Suchkriterium nur noch diese Felder sortieren. Dies sollte im Mittel deutlich schneller gehen. Einerseits fallen in jeder Iteration weitere Objekte raus, die schon sortiert sind und andererseit entstehen (wahrscheinlich) kleine Partionen, diese lassen sich dann deutlich schneller sortieren.
Zudem brauchst du weiterhin nur paarweise zu vergleichen.

Am meisten dürftest du allerdings davon profitieren, wenn du gleich sortiert einfügst. Wenn du ein Objekt bekommst, könntest du jede seiner Eigenschaften in je eine Liste (die dann konsistent gehalten werden müsste) sortiert einfügen. Sollte es dann zu einer Abfrage kommen, Liegt die Sortierung nach dem Kriterium schon vor. Bei einfügen sollte es in der Regel wenig Zeit kosten (sehen wir mal vom ersten hinzufügen eines Archivs ab), danach kommen doch eher geringe Mengen hinzu. Jeder Eintrag in einer solchen Liste kann dann noch einen Verweis auf das eigentliche Objekt enthalten (also ein Tupel aus dieser einen Eigenschaft nach der die Liste sortiert ist + Verweis auf das eigentliche Objekt).
Die ist allerdings ohne Frage keine speicher schonende Variante, da die Verweise sehr redundant abgelegt werden. Es muss aber eine solche Liste auch nicht für jedes Kriterium angelegt werden. Sicherlich macht es für Dinge, nach denen eher selten sortiert wird, bzw. die mit hoher Wahrscheinlichkeit gleich sind (z.B. SampleRate, Stimmung, ...) weniger Sinn eine solche Liste zu verwalten. Sollten diese Kriterien verwendet werden, muss halt vorher klassisch sortiert werden.

Nun alles zusammen:
Beim einfügen von neuen Daten, die wichtigen Eigenschaften sortiert ablegen (mit einem Verweis auf dieses neue Datum).
Wird nach mehreren Kriterien sortiert, so wird das Array zuerst nach dem primären Kriterium sortiert (sollte es eine Eigenschaft sein, zu der schon eine Sortierte Liste gehört, braucht man hier nichts zu tun und kann diese abrufen). In dieser Sortierung alle gleichen Elemente jeweils analog nach dem zweiten Kriterium sortieren (bis alles sortiert oder keine weiteren Kriterien vorhanden).

Gruß Der Unwissende

Gausi 27. Jul 2006 17:58

Re: Synonyme Bezeichnungen für eine Objekt-Eigenschaft
 
Es ist ja nicht das sortieren alleine. Dazu verwende ich auch die mitgelieferte Sort-Funktion der TObjectlist, wodurch sich das Sortieren auf kleine Mini-Funktionen (eben die Compare-Funktionen) beschränkt. Wenn ich den Code jetzt dahingehend erweitere, und Teile neu sortiere, ist das viel Coding-Aufwand.
Hinzu kommt, dass ich für die entsprechend sortierten Listen auch jeweils eine Binärsuche nach der sortierten Eigenschaft programmiert habe, und bei einem Treffer z.B. alle Objekte ausgebe, die die primäre Eigenschaft besitzen, oder aber die primäre UND sekundäre - da käme dann auch entsprechend Aufwand auf mich zu.

Ich brauche dieses variable Verfahren für folgendes:
  • Objekt-Liste sortieren nach variablen Kriterien
  • Weitere Liste erstellen, die eine Auflistung aller unterschiedlichen primären Eigenschaften hat (also z.B. Alle Interpreten)
  • bei Markierung eines Interpreten (z.B. Die Ärzte) (später auch Genre, Jahr, u.a.) erstens eine Auflistung aller unterschiedlichen sekundären Eigenschaften, die bei Objekten mit der gewählten primären Eigenschaft existieren (also alle Alben von den Ärzten, oder alle Künstler des Genres) UND eine Auflistung aller Objekte mit der gewählten primären Eigenschaft
  • Bei zusätzlicher Markierung einer sekundären Eigenschaft weitere Filterung der Objektanzeige, so dass nur Objekte angezeigt werden, die beide Eigenschaften haben (Z.B. Alle Titel von den Ärzten auf dem Album 13, oder alle Punkrock-Titel aus dem Jahr 2006)
Damit das in Echtzeit auch bei längeren Listen funktioniert, brauche ich eine Sortierung nach beiden Kriterien, und eine entsprechende Binärsuche...

Das Stichwort, was ich benötigte, war Getter/Setter :wall:. Ich hätte natürlich zuerst für jede Eigenschaft nen eigenen Setter/Getter geschrieben, aber wenn das auch in einem geht, ist das natürlich ne feine Sache :D

Ist doch richtig, dass die Getter-Methode einfach den Index-ten String aus dem Array zurückliefert, und der Setter einfach den String im Array neu setzt, oder? Also einfach
Delphi-Quellcode:
function GetString(Index: TMyClassStringIndex): String;
begin
  result := FStrings[Index];
end;

procedure SetString(Index: TMyClassStringIndex; const Value: string);
begin
  FStrings[Index] := Value;
end;
Jetzt muss ich zwar die Klasse etwas umbauen (das sind bis jetzt alles einfach nur Variablen, keine Propertys), aber der Rest des Projektes kann so bleiben.

Flocke 27. Jul 2006 18:06

Re: Synonyme Bezeichnungen für eine Objekt-Eigenschaft
 
Zitat:

Zitat von Gausi
Ist doch richtig, dass die Getter-Methode einfach den Index-ten String aus dem Array zurückliefert, und der Setter einfach den String im Array neu setzt, oder? Also einfach
Delphi-Quellcode:
function GetString(Index: TMyClassStringIndex): String;
begin
  result := FStrings[Index];
end;

procedure SetString(Index: TMyClassStringIndex; const Value: string);
begin
  FStrings[Index] := Value;
end;

Ja - so einfach ist das.

Gausi 17. Aug 2006 19:25

Re: Synonyme Bezeichnungen für eine Objekt-Eigenschaft
 
Danke nochmal dafür. Bin jetzt endlich dazu gekommen, das auch tatsächlich zu implementieren, und es läuft wunderbar. Nur eine Kleinigkeit für die Nachwelt:

Statt
Delphi-Quellcode:
 property Strings[Index: TMyClassStringIndex] read GetString write SetString;
muss es natürlich heißen
Delphi-Quellcode:
property Strings[Index: TMyClassStringIndex]: String read GetString write SetString;


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:14 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-2025 by Thomas Breitkreuz