Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung (https://www.delphipraxis.net/170097-quelltextbausteine-aehnlich-include-dateien-gegen-codedopplung.html)

RSE 30. Aug 2012 16:07

Delphi-Version: XE

"Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Hallo,

ich habe mir zur Vereinfachung folgende Methode programmiert:
Delphi-Quellcode:
class function TArrays.IndexOf<T>(const Arr: TArray<T>; Value: T): Integer;
begin
  for Result := High(Arr) downto 0 do
    if Arr[Result] = Value then
      Exit;
  Result := -1;
end;
Das funktioniert aufgrund des Vergleiches leider nur mit der Einschränkung
Delphi-Quellcode:
<T: class>
. Alle anderen "einfachen" Typen (Integer, String etc.) müsste ich durch überladene Versionen implementieren. Dabei ist der Quelltext im Rumpf der Methode immer identisch, lediglich die Typen im Kopf variieren. Man könnte also den Rumpf in eine Include auslagern, um ihn nicht für jeden weiteren Typ kopieren zu müssen. Allerdings erscheint es mir nicht sinnvoll so einen kleinen Codeschnipsel in eine eigene Datei zu packen. Wenn es eine Art definierbaren "Codebaustein" gäbe, der sich genauso verhält wie eine Include, dann wäre ich glücklich. Allerdings nehme ich an, dass es das nicht gibt. Fällt jemandem eine Alternative außer RTTI ein?

Das einzige, was mir einfällt, ist an einer zentralen Stelle im Programm eine mehrfach überladene Funktion
Delphi-Quellcode:
IsEqual(const FirstValue, SecondValue: T): Boolean;
für alle möglichen Typen bereitzustellen, die ich für solche Zwecke nutzen kann. Das führt allerdings auch zu schwer auffindbaren Fehlern, wenn ich einen Typ verwende, der noch zu keinem überladenen Typ kompatibel ist...

s.h.a.r.k 30. Aug 2012 16:09

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Schau dir mal die Comparer aus den Generics-Units an. Das hilft dir vielleicht bzgl. deinem Vergleich :wink:

[add]
So, habe gerade mal in meiner ArrayHelper-Klasse geschaut:
Delphi-Quellcode:
TComparer<T>.Default;
TEqualityComparer<T>.Default

RSE 30. Aug 2012 16:39

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Delphi-Quellcode:
TEqualityComparer<T>.Default
klingt ungefähr nach dem, was ich suche, aber das arbeitet mit RTTI und das möchte ich nicht. Wieso hat Embarcadero dort nicht für alle Basistypen überladen? Die haben doch den Überblick über all das, was man als normaler Programmierer übersieht. Für referenzierte Typen geht es generisch, und die restlichen sollte man alle (notfalls mit ein wenig Compilermagic) mit kompatiblen Basistypen erschlagen können. Beispielsweise sind ja Aufzählungstypen ordinal -> eine Version für Ordinaltypen würde reichen, ähnlich wie bei
Delphi-Quellcode:
Ord
.

Wenn es sowas nicht gibt, werde ich mich näher mit den Grenzen dessen beschäftigen, was ich selbst in der Richtung machen kann - Kann man irgendwie einen Parametertypen angeben, der typsicher (zwecks Überladen) alle Ordinaltypen akzeptiert und auf Gleichheit prüfen kann?

Uwe Raabe 30. Aug 2012 16:47

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Zitat:

Zitat von RSE (Beitrag 1180726)
Delphi-Quellcode:
TEqualityComparer<T>.Default
klingt ungefähr nach dem, was ich suche, aber das arbeitet mit RTTI und das möchte ich nicht.

Darf man fragen, wieso nicht?


Zitat:

Zitat von RSE (Beitrag 1180726)
Wieso hat Embarcadero dort nicht für alle Basistypen überladen?

Weil die obige Funktion das bereits erledigt?

RSE 30. Aug 2012 16:53

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Ist es nicht so, dass die RTTI einen gewissen Overhead hat, den man nicht bräuchte, wenn man mit überladenen Typen arbeitet? Diese Funktionalität ist bei mir an einer sehr zentralen Stelle und könnte somit durchaus auch in zeitkritischen Programmteilen Verwendung finden. Deshalb möchte ich diese Stelle nicht durch Verwendung von RTTI unnötig ausbremsen.

himitsu 30. Aug 2012 17:56

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Wie bemerkt, kannst du es eben nicht direkt vergleichen, ohne den generischen Parameter nicht auf einen vergleichbaren Typen einzugrenzen.

Also kannst du nur über die RTTI versuchen rauszubekommen, wie man es vergleicht, oder du nutzt den TComparer, bzw. IComparer, welcher es für dich vergleicht.

Delphi-Quellcode:
class function TArrays.IndexOf<T>(const Arr: TArray<T>; Value: T; Comparer: IComparer = nil): Integer;

Wird kein Comparer übergeben, versuchst du einen Default-Comparer zu erstellen und nutzt diesen.

shmia 30. Aug 2012 18:05

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Also ich glaube, Du gehst in die falsche Richtung.
Einerseits scheinst du in deiner Software sehr häufig Arrays von verschiedenen Datentypen einzusetzen.
Ich nehme mal an, dass man in deinem Code solche Deklarationen findet:
Delphi-Quellcode:
type
  TPlayer = record
    Name:string;
    Punkte: Integer;
    ...
  end;
  TPlayers = array of TPlayer;
Arrays haben eine relativ niedrige Abstraktionsebene.
Im Gegensatz zu Objekten sind Arrays völlig ungeschützt; jeder kann lesen & schreiben oder bei dynamischen Arrays die Grösse ändern.
Greift man mit dem Index versehentlich daneben, dann gibt es bei dynamischen Arrays eine Zugriffsverletzung oder ERangeError.

Dabei gibt es neben Arrays auch noch Listen, Queues, Stacks, usw.
Intern können Listen ja durchaus Arrays benützen; entscheidend ist aber wie sich Listen nach aussen "anfühlen".
Der Zugriff auf Listen ist geschützt und die IndexOf()-Methode keine grosse Sache.
Listen sind einfach etwas abstrakter als Arrays.

Arrays sind wie gesagt relativ primitive Datentypen.
Du versuchst diesen Mangel auszugleichen indem du programiertechnisch abeghobene Generics verwendest.
Listen scheinen in deiner Gedankenwelt gar nicht als Alternative zu Arrays zu existieren.
Dabei bist du nicht alleine - es gibt viele Leute hier im Forum, die den Sprung vom Array zur Liste nicht schaffen.

Abstraktionsebenen der Datentypen in Delphi (unvollständige Liste)
1.) ordinale Datentypen, Aufzählungstypen, Boolean, Integer, char, ...
2.) Sets, Arrays fester Grösse
3.) Strings, dynamische Arrays
4.) Records, Events, Variants
5.) Lists, stacks, queues, Trees
6.) Klassen, Objekte
7.) Interfaces, Streams
8.) Design Patterns, Meta Klassen, Generics, Closures, Reflection (=RTTI)
9.) Application Domain, Enterprise Domain Patterns
10.) Systeme, Dienste

Du nimmst also Arrays aus Ebene 3 und vermischst das mit Generics aus Ebene 8.
Dieser Bruch zwischen den Ebenen halte ich für problematisch.
Die Ebene 5 scheint bei Delphi Programmierern ganz allgemein unterrepräsentiert zu sein.

Ok, hier in meinem Beitrag geht es um (Programmier-)Philosophie und falls jemand versuchen sollte Gegenargumente anzubringen werde ich nicht antworten, denn das führt zu nichts.

RSE 30. Aug 2012 18:22

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Wenn ich mit einer Klasse arbeite, die eine Liste repräsentiert, bin ich zwar näher am Paradigma der Objektorientierung, aber kein bisschen weiter. Ich benutze diese Arrays für kleine Aufgaben, die den Aufwand einer eigenen Ableitung einer Liste nicht rechtfertigen. Dort wo ich sie verwende gibt es wesentlich weniger als 20 Quelltextzeilen mit den Arrays (incl. Definition, Parameter in Methodenköpfen etc.). Die Ableitung von TList würde dieses Maß bereits übersteigen. Würde ich mit einer generischen Liste arbeiten, bin ich wieder an der gleichen Stelle, hätte aber den Overhead des Instanzierens und Freigebens. Also bleibe ich für diese Stellen bei einfachen Arrays und halte ein paar Methoden vor, die mir das Leben damit noch mehr vereinfachen. Dadurch gibt es Arrays, die nur in 5 Quelltextzeilen auftauchen.

Im Moment ist das Überladen einer zentralen IsEqual-Funktion immernoch meine präferierte Lösung.

Sir Rufo 31. Aug 2012 00:23

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Wenn du dir eine Factory baust, die eine gewünschte Liste als Interface zurückgibt ist der Aufwand genau gleich wie bei einem Array. Also von daher braucht man sich nicht an den Arrays festklammern ;)

Furtbichler 31. Aug 2012 06:29

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Ich glaube, er will den Overhead und Footprint klein halten. Davon ausgehend, das die eleganten Lösungen seinem Ziel wiedersprechen, bleibt also nur, für jedes Array eine individuelle kleine Routine zu schreiben.

Allerdings scheint mir dann der Ansatz unglücklich, denn das Suchen in einem unsortierten Array ist ziemlich langsam: Je größer die Liste, desto drastischer die Performanceeinbußen.

Um in heterogenen Arrays nach einzelnen Objekten Strukturen zu suchen (ein Primärschlüssel vorausgesetzt), scheint eine Hashmap das Richtige zu sein. Zur Not wäre noch eine THashedStringList eine Möglichkeit, Schlüssel/Struktur-Paare (genauer: Schlüssel/Zeiger-Auf-Struktur-Paare) zum schnellen Suchen zu verwenden.

Uwe Raabe 31. Aug 2012 08:49

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Zitat:

Zitat von Furtbichler (Beitrag 1180797)
Allerdings scheint mir dann der Ansatz unglücklich, denn das Suchen in einem unsortierten Array ist ziemlich langsam: Je größer die Liste, desto drastischer die Performanceeinbußen.

In dem Fall bringt das vermutlich auch einen höheren Performancegewinn, als es das RTTI ausbremsen würde.

Furtbichler 31. Aug 2012 08:51

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Bei kleinen Arrays (N< ca. 20) versagen i.a. Optimierungsbestrebungen.

RSE 31. Aug 2012 08:59

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Zitat:

Zitat von Furtbichler (Beitrag 1180797)
Ich glaube, er will den Overhead und Footprint klein halten.

Genau das ist mein Ansinnen.
Zitat:

Zitat von Furtbichler (Beitrag 1180797)
Allerdings scheint mir dann der Ansatz unglücklich, denn das Suchen in einem unsortierten Array ist ziemlich langsam: Je größer die Liste, desto drastischer die Performanceeinbußen.

Da die Elemente wenige sind (in der Regel < 10), ist das kein Thema. In dieser Größenordnung wären Suchalgorithmen für sortierte Listen insgesamt sogar langsamer (incl. Verwaltungsaufwand wie z.B. sortiertes Einfügen gerechnet).

Ich habe gerade einen ersten Test gemacht, der die ganze Sache mit dem zentralen überladen einer IsEqual-Funktion hinfällig macht: Die Auswahl der korrekten überladenen Version erfolgt bevor die generischen Typen bekannt sind und somit gibt es keine überladene Version, die man mit diesen Argumenten aufrufen kann... Also bleibt mir nur, immer und überall diese ganze Funktionalität zu überladen statt eine generische Funktion zu nutzen. Die einzige Alternative scheinen die RTTI zu sein.

Also werde ich doch RTTI einbauen und diese mit ein paar meistverwendeten Typen überladen. Das steht bei jeder Methode einzeln an. Bei zeitkritischen Routinen muss ich also dran denken zu überprüfen, dass eine entsprechende überladene Version existiert.

Uwe Raabe 31. Aug 2012 11:15

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Zitat:

Zitat von Furtbichler (Beitrag 1180814)
Bei kleinen Arrays (N< ca. 20) versagen i.a. Optimierungsbestrebungen.

Deswegen hatte ich mich mit "In diesem Fall" ja auch auf deine Bemerkung "Je größer die Liste" bezogen. Aber es sind ja wohl immer nur recht überschaubare Datenmengen. Man kann eben nur schwer Optimierungsvorschläge machen, wenn man das gesamte Umfeld nicht kennt.

Elvis 31. Aug 2012 15:01

AW: "Quelltextbausteine" ähnlich include-Dateien gegen Codedopplung
 
Vorsicht mit Vorurteilen.
TEqualityComparer<T>.Default liefert dir eine Instant eines TEqualityComparer<T>, welche nur einmal erzeugt werden muss. (Die Kosten dafür an RTTI etc sind also irrelevant)
EMBT wird sich Mühe gegeben haben, um die jeweilige Implementierung, die sich TEqualityComparer<T> anhand des T pickt ordentlich zu optimieren.

Wenn man aber einen ganz kleinen Test schreibt, der ihn gegen einen einfachen Integer-Vergleich antreten lässt, würde man erwarten, dass der Code gleich viiieel langsamer wird, right?

Denkste!
Zitat:

call1 took 294 ms
call2 took 146 ms
Ich glaube du kannst dir den Stress getrost sparen...

Ändere die Integers zu Strings und es sieht noch entspannter aus:
Zitat:

call1 took 1082 ms
call2 took 947 ms

(Optimierungen in den Compiler Settings ausschalten, nicht dass er den ganzen Vergleich wegoptmiert :stupid: )
Delphi-Quellcode:
uses
  SysUtils,
  Diagnostics,
  Generics.Collections,
  Generics.Defaults;

function Test(callback : TProc) : TStopwatch;
var
  stopWatch : TStopwatch;
begin
  stopWatch := TStopwatch.StartNew();
  try
    callback();
  finally
    stopWatch.Stop();
  end;

  exit(stopWatch);
end;

const
  sampleSize = 10000000;
var
  call1, call2 : TStopwatch;
  list1, list2 : TList<Integer>;

  procedure FillTestData;
  var
    i : Integer;
  begin
    list1.Capacity := sampleSize;
    list2.Capacity := sampleSize;
    for i := 0 to sampleSize do
    begin
      list1.Add(Random(i * 10));
      list2.Add(Random(i * 100));
    end;
  end;
begin
    list1 := TList<Integer>.Create();
    try
      list2 := TList<Integer>.Create();
      try
        FillTestData();

        call1 := Test(procedure
        var
           i : Integer;
           dummy : Boolean;
        begin
          for i := 0 to sampleSize do
            dummy := TEqualityComparer<Integer>.Default.Equals(list1[i], list2[i]);
        end);

        call2 := Test(procedure
        var
           i    : Integer;
           dummy : Boolean;
        begin
          for i := 0 to sampleSize do
            dummy := list1[i] = list2[i];
        end);
      finally
        list2.Free();
      end;
    finally
      list1.Free();
    end;

  WriteLn('call1 took ', Trunc(call1.Elapsed.TotalMilliseconds), ' ms');
  WriteLn('call2 took ', Trunc(call2.Elapsed.TotalMilliseconds), ' ms');
 end.


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