operator overloading

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Arthur Dent
Beiträge: 21
Registriert: Mi 15. Feb 2012, 22:17

operator overloading

Beitrag von Arthur Dent »

Hallo Liebes Lazarus Forum!

Ich versuche grad zur gewöhnung an Pascal eine kleine numrische Algebra Bibliothek zu schreiben.
Ich fange mit einer Matrix-Klasse an. Leider bekomme ich beim Versuch Operatoren zu Überladen fiese exceptions (external-SIGSEV in zeile 44; unit 1) die für mich absolut unlesbar sind :cry:
Was hab ich hier bloß falsch Verstanden?

so sieht das Programm aus:

Code: Alles auswählen

 
program project1;
 
{$mode objfpc}{$H+}
 
uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, unit1
  { you can add units after this };
var
  mat1,mat2,mat3:TdoubleMatrix;
 
begin
  writeln('test prog' + sLineBreak);
  mat1:= TdoubleMatrix.create(2,2);
  mat2:= TdoubleMatrix.create(2);
  mat3:= mat1+mat2;
  readln;
end.
 


und so die unit mit der matrix klasse

Code: Alles auswählen

 
unit Unit1;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils;
 
// definition of a matrix consisting of double values
type
  TdoubleArray = array of array of double;
  TdoubleMatrix = class
    private
      // data
      rows,columns: Cardinal;
      dataArray: TdoubleArray;
    public
      // constructor signitures
      constructor create(m,n:Cardinal);overload;
      constructor create(size:Cardinal);overload;
      constructor create(inputArray: TdoubleArray);overload;
      // properties
      property toArray: TdoubleArray read dataArray;
      // Functions
    function toString:AnsiString;overload;
  end;
 
operator +(A,b:TdoubleMatrix):TdoubleMatrix;
 
 
implementation
 
  operator +(A,B:TdoubleMatrix):TdoubleMatrix;
  var
    i,j:integer;
    outMatrix:TDoubleMatrix;
  begin
    if (A.columns = B.columns) and (A.rows = b.rows) then
    begin
      outMatrix.create(A.columns,A.rows);
      for i:= 1 to A.rows do begin
        for j:= 1 to A.columns do begin
          outMatrix.dataArray[i,j]:=A.dataArray[i,j] + B.dataArray[i,j];
        end;
      end;
    end;
    result:=A;
  end;
 
  // construc by dimensions
  constructor TdoubleMatrix.Create(m,n:Cardinal);
  begin
    rows:= m;
    columns:= n;
    SetLength(dataArray,rows,columns);
  end;
 
  // constructor for unit matrix
  constructor TdoubleMatrix.Create(size:Cardinal);
  var
    i: cardinal;
  begin
    rows:= size;
    columns:= size;
    SetLength(dataArray,rows,columns);
    for i:= 0 to size-1 Do
    begin
      dataArray[i,i]:= 1.0;
    end;
  end;
 
  // constructor for arbitrarry matrix
  constructor TdoubleMatrix.Create(inputArray:TdoubleArray);
  begin
    columns:= Length(inputArray);
    rows:= Length(inputArray[0]);
    dataArray:= inputArray;
  end;
 
  function TdoubleMatrix.toString:AnsiString;
  ...
end.
 


Zu meinem Hintergrund:
Ich versuche mich seit fast einem Jahrzent mal wieder an Pascal. Meine letzten Erfahrungen damit wahren ein bisschen Delphi Programmierung im Informatik untericht in der Schule. In der Zwischenzeit hatte ich mit unterschiedlichen Objektorientierten Sprachen u.A. Java,Python,C++ zu tun. Bin also durchaus mit Grundkonzepten objektorientierten Designs vertraut. Allerdings bin ich kein Informatiker und würde mich auch nicht als Software entwickler oder Programmierer bezeichnen. Ich mache das eher im Hobby-Bereich.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: operator overloading

Beitrag von Mathias »

Code: Alles auswählen

  operator +(A,B:TdoubleMatrix):TdoubleMatrix;
  var
    i,j:integer;
    outMatrix:TDoubleMatrix;
  begin
    if (A.columns = B.columns) and (A.rows = b.rows) then
    begin
      outMatrix.create(A.columns,A.rows);
      for i:= 1 to A.rows do begin
        for j:= 1 to A.columns do begin
          outMatrix.dataArray[i,j]:=A.dataArray[i,j] + B.dataArray[i,j];
        end;
      end;
    end;
    result:=A;
  end;

Du erzeugst eine outMatrix, lädst diese mit Werten und dann.... ?
Als Result gibst dann A aus, wie es oben rein kommt.

Das ganze sieht auch gefährlich aus, was passiert, wen deine Matrizen unterschiedlich gross sind ? Könnte dein SIGSEV sein.

Ich habe da ein Matrixmultiplikation von OpenGL, arbeitet mit einer statischen Array (4x4), aber es sollte dir als Muster dienen.

Code: Alles auswählen

  TMatrix = class(TObject)
  private
    FMatrix: Tmat4x4;
  public
    constructor Create;
    procedure Identity;
    procedure Assign(m: TMatrix);
....
operator * (const m1, m2: Tmat4x4) res: Tmat4x4;
var
  i, j, k: integer;
begin
  for i := 0 to 3 do begin
    for j := 0 to 3 do begin
      Res[i, j] := 0;
      for k := 0 to 3 do begin
        Res[i, j] := Res[i, j] + m2[i, k] * m1[k, j];
      end;
    end;
  end;
end;


Die ganze Unit findest du hier: http://mathias1000.bplaced.net/Tutorial ... source.zip
Der Name der Unit ist oglMatrix.

Ich hoffe, das dir dies weiter hilft.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

diogenes
Beiträge: 200
Registriert: So 11. Jul 2010, 18:39
OS, Lazarus, FPC: Linux
CPU-Target: 64 Bit
Wohnort: Wien
Kontaktdaten:

Re: operator overloading

Beitrag von diogenes »

Und noch etwas: Klassen in Free Pascal haben keinen Referenzzähler eingebaut. Wenn man eine Instanz erzeugt, dann bleibt diese so lange stehen, bis man sie frei gibt. Bloß weil der Speicherplatz der Variable freigegeben wird, wird also kein ein Referenzzähler zurück gezählt und schon gar keine Instanz freigegeben. Wenn du also mit Operatoren arbeiten willst, dann nimm variablen vom Typ object und nicht class. Object-Variablen sind praktisch records mit Methoden. Damit funktioniert alles besser bis einwandfrei. Siehe auch die Sprachreferenz von Free Pascal
Ceterum censeo computatores per Pascal docendos esse.

Arthur Dent
Beiträge: 21
Registriert: Mi 15. Feb 2012, 22:17

Re: operator overloading

Beitrag von Arthur Dent »

Zunächst vielen Dank für deine Antwort. Leider kann ich mit deinem Beispiel ohne weitere erläuterungen nicht viel anfangen. Soweit ich das überblickt habe manipulierst du ja nur matritzen mit fester Größe ( maximal 4x4); Ich versuche allerdings mit beliebig großen Matritzen zu rechnen. Vermutlich werde ich mit keiner besonders effizienten Implementierung rauskommen, aber mir geht es hier in erster linie darum Verständnis für die Arbeit mit der Programmiersprache zu erlangen.

Du erzeugst eine outMatrix, lädst diese mit Werten und dann.... ?
Als Result gibst dann A aus, wie es oben rein kommt.


Hm, Ok
Die Zeile ist doch aus dem Ersten "Dummy" des überladenen Operators übrig. Ich wollte zunächst mal testen ob das überladen im allgemeinen funktioniert und habe einfach eines der Agumente zurückgegeben.

Das ganze sieht auch gefährlich aus, was passiert, wen deine Matrizen unterschiedlich gross sind ? Könnte dein SIGSEV sein.


Wenn die matritzen unterschiedlich groß wird sind wird auf grund der Abfrage gar nicht in die problematische Zeile gesprungen. In dem Fall muss eine natürlich eine sinnvolle exception geworfen werden. soweit war ich noch nicht.
Was genau ist hier gefährlich? Wie sieht eine bessere implentierung aus um das gewünschte ergebnis zu erreichen?

vielen Dank im Voraus!

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: operator overloading

Beitrag von wp_xyz »

Ich glaube, wenn du hier mit Klassen arbeitest, wird es unnötig kompliziert, weil du die Klasse die du mit A := B + C erzeugst auch durch einen expliziten Aufruf von A.Free wieder freigeben musst. Wesentlicher einfacher wird es, wenn du mit erweiterten Records arbeitest, für Records kann man heute auch Methoden schreiben.

diogenes
Beiträge: 200
Registriert: So 11. Jul 2010, 18:39
OS, Lazarus, FPC: Linux
CPU-Target: 64 Bit
Wohnort: Wien
Kontaktdaten:

Re: operator overloading

Beitrag von diogenes »

wp_xyz hat geschrieben:Ich glaube, wenn du hier mit Klassen arbeitest, wird es unnötig kompliziert, weil du die Klasse die du mit A := B + C erzeugst auch durch einen expliziten Aufruf von A.Free wieder freigeben musst. Wesentlicher einfacher wird es, wenn du mit erweiterten Records arbeitest, für Records kann man heute auch Methoden schreiben.

Das meine ich auch. Siehe meinen post oben :)
Ceterum censeo computatores per Pascal docendos esse.

Arthur Dent
Beiträge: 21
Registriert: Mi 15. Feb 2012, 22:17

Re: operator overloading

Beitrag von Arthur Dent »

@diogenes

sry, hatte deine antwort übersehen. Ich hatte tatsächlich zunächst angefangen das als object zu implementieren, bin dann aber irgendwie zu klassen übergegangen. weiß auch erlich gesagt gar nicht mehr genau warum. bin wahrscheinlich zu sehr durch java geprägt. da ist heißt halt alles class und über speicherverwaltung muss man wenig nachdenken ;)

werds also wieder mit object versuchen.

Vielen Dank!

diogenes
Beiträge: 200
Registriert: So 11. Jul 2010, 18:39
OS, Lazarus, FPC: Linux
CPU-Target: 64 Bit
Wohnort: Wien
Kontaktdaten:

Re: operator overloading

Beitrag von diogenes »

Bitteschön :)
Ceterum censeo computatores per Pascal docendos esse.

Arthur Dent
Beiträge: 21
Registriert: Mi 15. Feb 2012, 22:17

Re: operator overloading

Beitrag von Arthur Dent »

Hab grad nochmal in ruhe drauf geschaut und konnte das problem wie üblich auf meine eigene dummheit zurückführen. Folgender ausschnitt ist natürlich ziemlicher blödsinn!
Ich hol mir hier die arrayindizes als schleifenparameter und die starten wie üblich bei 0 und laufen bis länge-1 :oops:

Code: Alles auswählen

 
      for i:= 1 to A.rows do begin
        for j:= 1 to A.columns do begin
          outMatrix.dataArray[i,j]:=A.dataArray[i,j] + B.dataArray[i,j];
        end;
      end;
 


richtig wär also:

Code: Alles auswählen

 
      for i:= 0 to A.rows-1 do begin
        for j:= 0 to A.columns-1 do begin
          result.dataArray[i,j]:=A.dataArray[i,j] + B.dataArray[i,j];
        end;
      end;
 

diogenes
Beiträge: 200
Registriert: So 11. Jul 2010, 18:39
OS, Lazarus, FPC: Linux
CPU-Target: 64 Bit
Wohnort: Wien
Kontaktdaten:

Re: operator overloading

Beitrag von diogenes »

Nu, so dumm bist du nicht, weil das ist wohl schon jedem passiert :)
Ceterum censeo computatores per Pascal docendos esse.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: operator overloading

Beitrag von wp_xyz »

Du kannst dir als Ideenquelle auch die Unit matrix (in (fpc)/packages/rtl-extra) ansehen; da gibt es einiges an Operator-Overloading, allerdings nur bis max 4x4. Bei beliebig großen Matrizen wirst du dann auch allgemeine Algorithmen für Determinanente, inverse Matrix, Eigenwerte etc benötigen; auch das gibt es im fpc-Verzeichnis unter packages/numlib (ich habe vor einiger Zeit dazu das ins wiki geschrieben: http://wiki.lazarus.freepascal.org/NumLib_Documentation).

Antworten