operator overloading

Für Fragen von Einsteigern und Programmieranfängern...

operator overloading

Beitragvon Arthur Dent » 11. Nov 2017, 21:08 operator overloading

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.
Arthur Dent
 
Beiträge: 5
Registriert: 15. Feb 2012, 22:17

Beitragvon Mathias » 11. Nov 2017, 21:53 Re: operator overloading

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 gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 3189
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

Beitragvon diogenes » 11. Nov 2017, 22:00 Re: operator overloading

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.
diogenes
 
Beiträge: 160
Registriert: 11. Jul 2010, 17:39
Wohnort: Wien
OS, Lazarus, FPC: Linux (L 0.9.xx FPC 2.2.z) | 
CPU-Target: 32Bit
Nach oben

Beitragvon Arthur Dent » 11. Nov 2017, 22:17 Re: operator overloading

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!
Arthur Dent
 
Beiträge: 5
Registriert: 15. Feb 2012, 22:17

Beitragvon wp_xyz » 11. Nov 2017, 22:24 Re: operator overloading

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.
wp_xyz
 
Beiträge: 2249
Registriert: 8. Apr 2011, 08:01

Beitragvon diogenes » 11. Nov 2017, 22:26 Re: operator overloading

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.
diogenes
 
Beiträge: 160
Registriert: 11. Jul 2010, 17:39
Wohnort: Wien
OS, Lazarus, FPC: Linux (L 0.9.xx FPC 2.2.z) | 
CPU-Target: 32Bit
Nach oben

Beitragvon Arthur Dent » 11. Nov 2017, 22:39 Re: operator overloading

@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!
Arthur Dent
 
Beiträge: 5
Registriert: 15. Feb 2012, 22:17

Beitragvon diogenes » 11. Nov 2017, 22:41 Re: operator overloading

Bitteschön :)
Ceterum censeo computatores per Pascal docendos esse.
diogenes
 
Beiträge: 160
Registriert: 11. Jul 2010, 17:39
Wohnort: Wien
OS, Lazarus, FPC: Linux (L 0.9.xx FPC 2.2.z) | 
CPU-Target: 32Bit
Nach oben

Beitragvon Arthur Dent » 12. Nov 2017, 10:33 Re: operator overloading

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;
 
Arthur Dent
 
Beiträge: 5
Registriert: 15. Feb 2012, 22:17

Beitragvon diogenes » 12. Nov 2017, 10:49 Re: operator overloading

Nu, so dumm bist du nicht, weil das ist wohl schon jedem passiert :)
Ceterum censeo computatores per Pascal docendos esse.
diogenes
 
Beiträge: 160
Registriert: 11. Jul 2010, 17:39
Wohnort: Wien
OS, Lazarus, FPC: Linux (L 0.9.xx FPC 2.2.z) | 
CPU-Target: 32Bit
Nach oben

Beitragvon wp_xyz » 12. Nov 2017, 10:49 Re: operator overloading

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).
wp_xyz
 
Beiträge: 2249
Registriert: 8. Apr 2011, 08:01

• Themenende •

Zurück zu Einsteigerfragen



Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast

cron
porpoises-institution
accuracy-worried