Dekalartion von Prozeduren/Funktionen

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Benutzeravatar
Beach
Lazarusforum e. V.
Beiträge: 60
Registriert: Di 2. Nov 2021, 22:41
OS, Lazarus, FPC: Lazarus 3.0RC1 (rev lazarus_3_0_RC1-10-gfe49fef4fc) FPC 3.2.2 x86_64-win64-win32
CPU-Target: 64Bit
Wohnort: Hunsrück

Dekalartion von Prozeduren/Funktionen

Beitrag von Beach »

Achtung, jetzt kommt eine Einsteigerfrage aus dem untersten Level. Aber ich habe einfach festgestellt das ich hier eine deutliche Wissenslücke habe.

Ich habe ein kleines Programm erstellt, welches beim Klick auf einen Button eine TXT Datei einliest, und u.a. die darin enthaltenen Daten in einem 2D Array zurückliefert.
Nun möchte ich diese einfach in einem Memo ausgeben. Dazu habe ich eine kleine Prozedur erstellt, die nun selbst nochmal eine 2. Prozedur aufrufen soll welche mit diesen Daten ein wenig rechnet. Beim Aufruf dieser 2. Prozedur hakt es nun -> "Identifier not found"

In etwa so:
Wobei es mir eher um das Prinzip als um die konkreten Funktionen hier geht :

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
  StrListERG: TStringList;

begin
  if OpenDialog1.Execute then
  begin

    ZeissERG := TClassZeissERG.Create;

    StrListERG := TStringList.Create;
    try
      StrListERG.LoadFromFile(UTF8TOAnsi(OpenDialog1.Filename));
    except
      begin
        StatusBar1.Panels[0].Text := 'Kann das File nicht laden';
        DataLoaded[erg] := False;
      end;
    end;
    StatusBar1.Panels[0].Text := 'Erfolgreich geladen';
    ERGValues := ZeissERG.Analyse(StrListERG);   // Aufruf der Klassenfunktion funktioniert problemlos
 
       printArray2D(ERGValues.Data);                    // Aufruf der Prozedur im Mainform -> Funktioniert

    end
    else
    begin
      StatusBar1.Panels[2].Text := ERGValues.ErrorText;
    end;

    // Aufräumen, Aufgerufene Klasse und Stringlist

    FreeAndNil(ZeissERG);
    FreeAndNil(StrListERG);

  end;   
    
Die Prozedur printArray2D() und CalcDistance() ist in der Mainform wie folgt definiert:

Code: Alles auswählen

Procedure printArray2D(Data: TStringArray2D);
var
  i, j, k: integer;
  a, b, c: string;
begin
  for i := low(Data) to high(Data) do
  begin
    for j := low(Data[i]) to high(Data[i]) do
    begin
      a := a + 'i=' + IntToStr(i) + ' j=' + IntToStr(j) + ': ' + Data[i][j]+'  -  ';
    end;
    Form1.Memo1.Lines.Add(a);
    a := '';
    CalcDistance( i, Data );    // Aufruf der 2. Prozedur -> Fehlermeldung
  end;
end;


Procedure CalcDistance( start: integer; Data: TStringArray2D );
var
  i, j, k: integer;
  a, b, c: string;
  d0, s0, x0, y0, z0 : double;  // Die erste Kugel
  d1, s1, x1, y1, z1 : double;  // Die zweite Kugel
  m, n : double;  // Berechnete Werte

begin
  a := '';
    // Startkugel
    x0 := StrToFloat( Data[start][1] );      // X Koordinate
    y0 := StrToFloat( Data[start][2] );      // Y Koordinate
    z0 := StrToFloat( Data[start][3] );      // Z Koordinate
    d0 := StrToFloat( Data[start][4] );      // Kugeldurchmesser
    s0 := StrToFloat( Data[start][0] );      // Sollwert
    // Endkugel
    x1 := StrToFloat( Data[start+1][1] );      // X Koordinate
    y1 := StrToFloat( Data[start+1][2] );      // Y Koordinate
    z1 := StrToFloat( Data[start+1][3] );      // Z Koordinate
    d1 := StrToFloat( Data[start+1][4] );      // Kugeldurchmesser
    s1 := StrToFloat( Data[start+1][0] );      // Sollwert

    m := sqrt( sqr( x1 - x0) + sqr( y1 - y0) + sqr( z1 - z0) ); // Distanz im Raum
    n := s1 - m;  // Sollwert minus Istwert Abstand

    a := a + 'Sollwert =' + FloatToStr(s1) +
             ' Istwert =' + FloatToStr(m) +
             ' Differenz: ' + Data[i][j];

    Form1.Memo1.Lines.Add(a);
    Form1.Memo1.Lines.Add(' ');
end;
Wann deklariere ich eine Prozedur mit z.B.

Code: Alles auswählen

Procedure printArray2D(Data: TStringArray2D);
und wann muß ich diese als Teil der Klasse z.B. Form1 mit

Code: Alles auswählen

 Procedure Form1.printArray2D(Data: TStringArray2D);
deklarieren und wo liegt genau der Unterschied in der Anwendung bzw wann kann man wie auf diese zugreifen?

Sorry für diese Frage, aber ich kapiere es einfach nicht.
MfG
Beach

Shit happens... Always in my shift

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

Re: Dekalartion von Prozeduren/Funktionen

Beitrag von wp_xyz »

Wenn ich dein PrintArray2D durchscrolle, sehe ich Bezeichner wie "Form1". Das heißt, du brauchst in dieser Prozedur etwas, was sich in Form1 befindet. Form1 ist eine Instanz der Klasse TForm1. Von dieser Klasse kannst du eine zweite Instanz erzeugen, die heißt vielleicht "Form1a". Nun funktioniert aber PrintArray2D nicht mehr - denn dieses arbeitet nur mit Form1, und wenn es ein Form1 evtl gar nicht gibt, dann stürzt sogar das Programm ab.

Immer wenn du in einer Prozedur etwas aus einer Klasseninstanz brauchst, hast du ein starkes Indiz, die Prozedur zu einer "Methode" zu machen, d.h. sie im Kontext dieser Klasse zu deklarieren. Denn nun ist es egal, wie die Instanz heißt, und du kannst - nein: musst sogar - das "Form1" weglassen.

Code: Alles auswählen

Procedure TForm1.PrintArray2D(Data: TStringArray2D);
var
  i, j, k: integer;
  a, b, c: string;
begin
  for i := low(Data) to high(Data) do
  begin
    for j := low(Data[i]) to high(Data[i]) do
    begin
      a := a + 'i=' + IntToStr(i) + ' j=' + IntToStr(j) + ': ' + Data[i][j]+'  -  ';
    end;
    Memo1.Lines.Add(a);
    a := '';
    CalcDistance( i, Data );    // Aufruf der 2. Prozedur -> Fehlermeldung
  end;
end;
Andererseits könntest du das Problem weiter verallgemeinern, denn die Zeile "Memo1.Lines.Add(a)" ist das einzige, was du von der TForm1-Instanz brauchst. Es könnte ja sein, dass du dieselben Daten zusätzlich in eine Datei schreiben möchtest, oder ein anderes Formular hast, in dem dieselben Daten in einem anderen Memo angezeigt werden sollen. Es bietet sich an, das Memo ganz aus PrintArray2D herauszunehmen und in eine allgemein verwendbare StringListe zu schreiben. Eine StringList hat den Vorteil, dass du sie einfach den Lines jedes beliebigen Memos zuweisen kannst, oder du kannst sie mit der Methode SaveToFile in eine Datei speichern etc. etc.

Wenn Memo1 nicht mehr in PrintArray2D vorkommt, dann kannst du diese Prozedur wieder aus der Klasse TForm1 herausnehmen, denn sie soll ja vielfältig einsetzbar werden. Es muss nun ein zusätzlicher Parameter bereitgestellt werden für die Liste, die die Daten statt des Memos empfängt.

Code: Alles auswählen

Procedure PrintArray2D(Data: TStringArray2D; List: TStrings);
var
  i, j, k: integer;
  a, b, c: string;
begin
  for i := low(Data) to high(Data) do
  begin
    for j := low(Data[i]) to high(Data[i]) do
    begin
      a := a + 'i=' + IntToStr(i) + ' j=' + IntToStr(j) + ': ' + Data[i][j]+'  -  ';
    end;
    List.Add(a);
    a := '';
    CalcDistance( i, Data, List );    // Aufruf der 2. Prozedur -> Fehlermeldung
  end;
end;
Ich habe oben von TStringList gesprochen, besser ist es aber - wie in dem Code-Beispiel - die Liste als deren Vorgänger, TStrings, zu deklarieren. Da TMemo.Lines von demselben Typ ist, kann man beim Aufruf direkt Memo1.Lines dafür einsetzen. Dafür schreibst du wieder eine Methode für TForm1 - damit es keine Namenskollision mit der einfachen PrintArray2D gibt nenne ich sie etwas anders -:

Code: Alles auswählen

procedure TForm1.ShowArray2DInMemo(Data: TStringArray2D);
begin
  PrintArray2D(Data, Memo1.Lines);
end;
CalcDistance solltest du dann genauso behandeln, denn es ist genauso aufgebaut.

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

Re: Dekalartion von Prozeduren/Funktionen

Beitrag von wp_xyz »

Beach hat geschrieben: Do 18. Nov 2021, 22:44 und wo liegt genau der Unterschied in der Anwendung bzw wann kann man wie auf diese zugreifen?
Das habe ich noch nicht beantwortet:

Eine "normale Prozedur" wie PrintArray2D(...) kannst du überall genauso aufrufen. So wie man es vor OOP machte.

Eine "Methode" (also eine Prozedur die innerhalb einer Klasse deklariert ist) kannst du innerhalb der Klasse ebenfalls direkt aufrufen, denn die Klasse weiß ja, dass die Prozedur mit zu ihr gehört. Von außerhalb musst du aber den Variablennamen der Instanz voranstellen, so wie bei Form1.PrintArray2D(...). Das wirkt wie ein zusätzliches Argument für die Prozedur.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1647
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Dekalartion von Prozeduren/Funktionen

Beitrag von fliegermichl »

@wp_xyz. Deine Antwort ist zwar korrekt, aber meines Erachtens etwas unübersichtlich. Ich will mal versuchen, es etwas simpler zu machen.

Code: Alles auswählen

type
 TTest = class
  procedure x;
 end;
 
 procedure y;
 
var t1 : TTest;
 
implementation

procedure TTest.x;
begin
 // Wenn ich jetzt auf t1 zugreife, gibt es einen Fehler falls unten der Aufruf von t1 := TTest.Create; fehlen würde
 writeln('x');
 y; // <- hier wird die Klassenlose Prozedur y aufgerufen.
end;

procedure y;
begin
 writeln('y');
end;

var t2 : TTest; 
begin
 t1 := TTest.Create;
 t2 := TTest.Create;
 t1.x;
 t2.x;
 y;
 t1.Free;
 t2.Free;
end.
Die Methode x gehört zu der Klasse TTest. Da ich bei der Implementation nicht weiss, welche "Instanz" also Variable später von diesem Typ definiert wird (im Beispiel t1 und t2), kann ich darauf auch nicht zugreifen. Blöderweise legt Lazarus bei Formularen immer automatisch eine globale Variable dieses Typs an (Form1)

y ist eine einfache Prozedur, die keiner Klasse angehört. Deswegen kann diese sowohl innerhalb als auch ausserhalb von Klassenmethoden aufgerufen werden.

Benutzeravatar
kupferstecher
Beiträge: 434
Registriert: Do 17. Nov 2016, 11:52

Re: Dekalartion von Prozeduren/Funktionen

Beitrag von kupferstecher »

Beach hat geschrieben: Do 18. Nov 2021, 22:44 Dazu habe ich eine kleine Prozedur erstellt, die nun selbst nochmal eine 2. Prozedur aufrufen soll welche mit diesen Daten ein wenig rechnet. Beim Aufruf dieser 2. Prozedur hakt es nun -> "Identifier not found"
Wenn beide Prozeduren in unit1 definiert sind, müsste sie vom Compiler auch gefunden werden. Kann es sein, dass du die Prozeduren nicht im Interface-Teil der Unit deklariert hast? Dann kennt der Compiler and der Stelle, wo du CalcDistance aufrufst, selbige noch gar nicht.

Benutzeravatar
Beach
Lazarusforum e. V.
Beiträge: 60
Registriert: Di 2. Nov 2021, 22:41
OS, Lazarus, FPC: Lazarus 3.0RC1 (rev lazarus_3_0_RC1-10-gfe49fef4fc) FPC 3.2.2 x86_64-win64-win32
CPU-Target: 64Bit
Wohnort: Hunsrück

Re: Dekalartion von Prozeduren/Funktionen

Beitrag von Beach »

Vielen Dank für die ausführlichen Antworten. Hat schon etwas Licht in mein Dunkel gebracht. Werde mir das bald nochmal genauer durchlesen. Hab leider nicht so die Zeit und den Nerv wie ich mir das für das Jahresende erhofft hatte. :roll:
MfG
Beach

Shit happens... Always in my shift

Antworten