Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Vektorklasse mit echten Operatoren (https://www.delphipraxis.net/179570-vektorklasse-mit-echten-operatoren.html)

cltom 17. Mär 2014 12:09

Delphi-Version: XE2

Vektorklasse mit echten Operatoren
 
Hallo,

das Thema gibt es sicher schon, ich konnte es aber nicht finden. Irgendwo hab ich hier gesehen, wie man eine Klasse für Vektorrechnung so umsetzt, dass Operatoren wie +/-/x/... richtig interpretiert werden, sodass man nicht schreiben muss: cVektor := vadd(aVector, bVector) sondern einfach cVektor = aVektor + bVektor und die Funktion rechnet dann entsprechend.

sorry, wenn es jetzt doch leicht zu finden ist und danke
gruß
cltom

Namenloser 17. Mär 2014 12:14

AW: Vektorklasse mit echten Operatoren
 
Das geht nur mit Records, nicht mit Klassen, und man nennt das Überladene Operatoren. In dem Artikel steht eigentlich alles drin, was man braucht.

Panthrax 17. Mär 2014 13:30

AW: Vektorklasse mit echten Operatoren
 
Eine Beispiel-Implementierung um einDreieck um einen Punkt zu drehen; die Verwendung:
Delphi-Quellcode:
var
  Alpha: Extended;
  O, X, Y, Z: TVector2D;
  U, V, W: TPoint;
begin
  Alpha := 0; // Drehwinkel
  O := TVector2D.New(200, 200); // Ortsvektor zum Drehpunkt
  X := TVector2D.New(-80, 0); // Dreieckspunkte relativ zum Drehpunkt
  Y := TVector2D.New(0, -80);
  Z := TVector2D.New(80, 0);

  { Das Dreieck erst drehen, dann um den Ortsvektor verschieben; denn:
  O + X.Rotated(Alpha) <> (O + X).Rotated(Alpha). }
  U := O + X.Rotated(Alpha);
  V := O + Y.Rotated(Alpha);
  W := O + Z.Rotated(Alpha);

  Image1.Canvas.Polygon([U, V, W]);
end;

Uwe Raabe 17. Mär 2014 13:47

AW: Vektorklasse mit echten Operatoren
 
Ich hatte sowas schon mal probeweise für zweidimensionale Vektoren implementiert, das aber später aus Performancegründen nicht weiter verfolgt. Im 3D-Bereich gibt es dann allerdings zwei Arten der Multiplikation zweier Vektoren. Leider geht das Overloading der Operatoren nicht so weit, daß man dort noch nach dem Ergebnistyp unterscheidet. In dem Fall würde das Kreuzprodukt dann wohl aus der Semantik rausfallen.

Delphi-Quellcode:
const
  NativeFormat: TFormatSettings = (DecimalSeparator: '.');

type
  TVektor = record
    X, Y: Extended;
    class operator Add(A, B: TVektor): TVektor;
    class operator Divide(A: TVektor; B: Extended): TVektor;
    class operator Equal(A, B: TVektor): Boolean;
    class operator Explicit(A: TVektor): string;
    class operator Implicit(A: TVektor): string;
    class operator Multiply(A: Extended; B: TVektor): TVektor;
    class operator Multiply(A: TVektor; B: Extended): TVektor;
    class operator Multiply(A: TVektor; B: TVektor): Extended;
    class operator Negative(A: TVektor): TVektor;
    class operator NotEqual(A, B: TVektor): Boolean;
    class operator Positive(A: TVektor): TVektor;
    class operator Subtract(A, B: TVektor): TVektor;
  end;

class operator TVektor.Add(A, B: TVektor): TVektor;
begin
  result.X := A.X + B.X;
  result.Y := A.Y + B.Y;
end;

class operator TVektor.Divide(A: TVektor; B: Extended): TVektor;
begin
  result.X := A.X / B;
  result.Y := A.Y / B;
end;

class operator TVektor.Equal(A, B: TVektor): Boolean;
begin
  result := SameValue(A.X, B.X) and SameValue(A.Y, B.Y);
end;

class operator TVektor.Explicit(A: TVektor): string;
begin
  result := Format('(%1.3f, %1.3f)', [A.X, A.Y], NativeFormat);
end;

class operator TVektor.Implicit(A: TVektor): string;
begin
  result := string(A);
end;

class operator TVektor.Multiply(A: Extended; B: TVektor): TVektor;
begin
  result.X := A * B.X;
  result.Y := A * B.Y;
end;

class operator TVektor.Multiply(A: TVektor; B: Extended): TVektor;
begin
  result.X := A.X * B;
  result.Y := A.Y * B;
end;

class operator TVektor.Multiply(A, B: TVektor): Extended;
begin
  result := A.X * B.X + A.Y * B.Y;
end;

class operator TVektor.Negative(A: TVektor): TVektor;
begin
  result.X := -A.X;
  result.Y := -A.Y;
end;

class operator TVektor.NotEqual(A, B: TVektor): Boolean;
begin
  result := not SameValue(A, B);
end;

class operator TVektor.Positive(A: TVektor): TVektor;
begin
  result := A;
end;

class operator TVektor.Subtract(A, B: TVektor): TVektor;
begin
  result.X := A.X - B.X;
  result.Y := A.Y - B.Y;
end;

BUG 17. Mär 2014 14:11

AW: Vektorklasse mit echten Operatoren
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1252255)
Im 3D-Bereich gibt es dann allerdings zwei Arten der Multiplikation zweier Vektoren.

Zusätzlich zu Kreuzprodukt und Skalarprodukt gibt es noch die Elemente-weise Multiplikation, die ist je nach Anwendung auch nicht so uninteressant.

Der schöne Günther 17. Mär 2014 14:55

AW: Vektorklasse mit echten Operatoren
 
Wie sieht es eigentlich mit dem "Nextgen"-Compiler aus? Da müsste doch für Klassen mittlerweile volle Operator-Überladung funktionieren, oder?

Uwe Raabe 17. Mär 2014 15:20

AW: Vektorklasse mit echten Operatoren
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1252266)
Wie sieht es eigentlich mit dem "Nextgen"-Compiler aus? Da müsste doch für Klassen mittlerweile volle Operator-Überladung funktionieren, oder?

Was bringt dich zu der Annahme?

Namenloser 17. Mär 2014 15:42

AW: Vektorklasse mit echten Operatoren
 
Ich hatte das Kreuzprodukt mal mit dem Divisionsoperator realisiert, da dieser bei Vektoren sonst keine sinnvolle Verwendung hat.

Uwe Raabe 17. Mär 2014 15:48

AW: Vektorklasse mit echten Operatoren
 
Zitat:

Zitat von Namenloser (Beitrag 1252271)
Ich hatte das Kreuzprodukt mal mit dem Divisionsoperator realisiert, da dieser bei Vektoren sonst keine sinnvolle Verwendung hat.

Die Idee hatte ich auch schon, aber ein Hauptgrund für die Verwendung des Operatoren ist je eigentlich eine bessere Lesbarkeit.

Der schöne Günther 17. Mär 2014 15:48

AW: Vektorklasse mit echten Operatoren
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1252267)
Zitat:

Zitat von Der schöne Günther (Beitrag 1252266)
Wie sieht es eigentlich mit dem "Nextgen"-Compiler aus? Da müsste doch für Klassen mittlerweile volle Operator-Überladung funktionieren, oder?

Was bringt dich zu der Annahme?

Automatische Referenzzählung:
Delphi-Quellcode:
var
   obj1, obj2: TMyObject;
begin
   obj2 := obj1 + obj1 + obj1;
end;
Allein für die erste "Addition" entsteht ein vom Programmierer nicht referenzierbares "Zwischenobjekt". Der Programmierer kann es nicht explizit freigeben und es liegt auf Ewig unbenutzt im Speicher.

Deshalb gibt es im "alten" Delphi-Compiler keine Operator-Überladung für Klassen. Dachte ich.

Mit ARC hat man dieses Problem ja nicht.

Anscheinend habe ich sogar Recht:
Zitat:

A very interesting side effect of using ARC memory management is that the compiler can handle the lifetime of temporary objects returned by functions. One specific case is that of temporary objects returned by operators. In fact, a brand new feature of the new Delphi compiler is the ability to define operators for classes, with the same syntax and model that has been available for records since Delphi 2006.

Note: The following code example works with the Delphi mobile (iOS) compilers, but cannot be compiled by the Delphi desktop compilers.
(Quelle: Embarcadero Docwiki)


Mann, gebt mir endlich den "Nextgen"-Compiler für Windows 8-)

himitsu 17. Mär 2014 15:57

AW: Vektorklasse mit echten Operatoren
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1252266)
Wie sieht es eigentlich mit dem "Nextgen"-Compiler aus? Da müsste doch für Klassen mittlerweile volle Operator-Überladung funktionieren, oder?

Theoretisch hätte es jetzt auch schon für Interfaces gehen können. Und ja, beim ARC (NextGen) könnte es funktionieren,
wenn die endlich mal meine Wünsche implementiert hätten, die seit Jahren im QC rumgammeln.

Der Grund, warum es bei Klassen nicht geht/ging, liegt darin begründet, daß dort keine automatische Speicherverwaltung existiert.

Delphi-Quellcode:
x := a * b + c;
// oder
x := a + b + c;
// [add] und selbst das einfache Nächste geht nicht
x := a + b;
Hier würden ja temporäre Variablen/Instanzen erstellt.
- das Ergebnis von a*b und a+b
- und das Ergebnis von t+c
Aber keiner fühlt sich verantwortlich diese Instanzen freizugeben.


Und es gibt noch ein paar Problemchen, denn es gibt kein Copy-Event, auf welches man per Klassenoperator reagieren kann.
Bei
Delphi-Quellcode:
X := Y;
kann man mit Klassen und vorallem ohne Referenzzählung nichts richtig behandeln.

Ich hatte mal versucht Interfaces in den Operator-Record zu packen, aber das war eine Heiden Arbeit und so weit ich mich erinnere, gab es dennoch ein paar Problemchen. (wofür ich gerne das Copy-Event haben wollte)
[add] http://www.delphipraxis.net/128555-i...athlib%5D.html

BUG 17. Mär 2014 16:11

AW: Vektorklasse mit echten Operatoren
 
Zitat:

Zitat von himitsu (Beitrag 1252274)
Hier würden ja temporäre Variablen/Instanzen erstellt.
- das Ergebnis von a*b
- und das Ergebnis von t+c
Aber keiner fühlt sich verantwortlich diese Instanzen freizugeben.

Abgesehen davon: Das wären bei Klassen zwei Allokationen und Freigaben auf dem Heap.
Das kostet potenziell ein Vielfaches von dem Aufwand für die eigentliche Operation. Für größere Datenstrukturen (große Matrizen) könnte das egal sein, aber für kleine Vektoren ist das nichts.

himitsu 17. Mär 2014 16:34

AW: Vektorklasse mit echten Operatoren
 
FastMM hat für kleine Speicherstücken eh fast immer schon einige Blöcke bereitwillig daliegen.

Ja, es dauert zwar immernoch ein "paar" Prozessortakte mehr, bis man sich einen freien Block rausgesucht und als belegt mariert hat, aber zum Glück ist das immernoch viel schneller, als es erst beim Windows zu beantragen.


Selbst wenn das ml für Objekte/Interfaces implementiert ist, dann kann man für kleinere Strukturen immernoch die Records verwenden. :D


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