[gelöst] Wie bekomme ich Procedure in Array und Record rein?

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

[gelöst] Wie bekomme ich Procedure in Array und Record rein?

Beitrag von Erwin »

Hallo Zusammen.

Las vorhin im anderem Thread, dass man sogar Procedure in Array rein tun kann? Wahnsinn. Das lässt mir jetzt keine Ruhe. Leider gibt es dabei bei der Suche zu viele Treffer. Und das übliche

Code: Alles auswählen

var
  ProzeArray: Array[0..2] of procedure=(FMachFormBlau, FMachFormRot, FMachFormGelb);
Klappt da nicht. Der Compiler bzw. Debugger (?) meckert da herum. Auch wenn ich 'Form1.' davor setzte, meckert er.

Das Beispiel aus dem anderem Thread geht zwar ...

Code: Alles auswählen

  ProzeArray: Array[0..2] of procedure(i:Integer);
... aber ich weiß da weder was das Bedeutet bzw. soll? Noch wie ich dann meine Procedure in die Array bekommen soll?

Hoffe es sind nur Kleinigkeiten ,die mir da an Codes fehlt?

Danke,

Gruß, Erwin.
Zuletzt geändert von Erwin am So 26. Nov 2023, 15:46, insgesamt 1-mal geändert.
Lazarus 2.2.0 / FP 3.2.4

Soner
Beiträge: 624
Registriert: Do 27. Sep 2012, 00:07
OS, Lazarus, FPC: Win10Pro-64Bit, Immer letzte Lazarus Release mit SVN-Fixes
CPU-Target: x86_64-win64
Wohnort: Hamburg

Re: Wie bekomme ich Procedure in Array rein?

Beitrag von Soner »

Das ist nichts weiter als Type-Deklaration, du hast Parametertypen vergessen. Richtig ist so:

Code: Alles auswählen

type
  TProzeArray= Array[0..2] of procedure(FMachFormBlau, FMachFormRot, FMachFormGelb: integer); // integer ist nur bespiel, du kannst jeder Parameter beliege Type geben.
var
  ProzeArray: TProzeArray; 
oder auch so, dann verstehst du es vielleicht besser:

Code: Alles auswählen

type
  TMeinProc = procedure(FMachFormBlau, FMachFormRot, FMachFormGelb: integer);
  TProzeArray= Array[0..2] of TMeinProc;
var
  ProzeArray: TProzeArray; 
Wenn deine Prozeduren in einer Klasse sind dann muss du am Ende "of object" hinzufügen:

Code: Alles auswählen

type
  TMeinProc = procedure(FMachFormBlau, FMachFormRot, FMachFormGelb: integer) of object; 
  TProzeArray= Array[0..2] of TMeinProc;
var
  ProzeArray: TProzeArray; 

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Wie bekomme ich Procedure in Array rein?

Beitrag von Erwin »

Danke, Soner.

Nun klappt es zumindest mit dem Erstellen.

Aber zum einen verwirrt mich das mit dem Integer, dem Beispiel. Habe statt Integer es mit String versucht. Scheint also total egal zu sein? Wie kommt das? Ergibt das einen Sinn?

Das andere Problem, wie rufe ich es auf, die Prozedure im Array? Diese 2 Versuche haben jedenfalls nicht geklappt bei mir:

Code: Alles auswählen

ProzeArray[1];
ProzeArray[FMachFormRot];
Was übersehe/vergesse ich?
Lazarus 2.2.0 / FP 3.2.4

Soner
Beiträge: 624
Registriert: Do 27. Sep 2012, 00:07
OS, Lazarus, FPC: Win10Pro-64Bit, Immer letzte Lazarus Release mit SVN-Fixes
CPU-Target: x86_64-win64
Wohnort: Hamburg

Re: Wie bekomme ich Procedure in Array rein?

Beitrag von Soner »

Erwin hat geschrieben:
Sa 25. Nov 2023, 21:54
Danke, Soner.

Nun klappt es zumindest mit dem Erstellen.

Aber zum einen verwirrt mich das mit dem Integer, dem Beispiel. Habe statt Integer es mit String versucht. Scheint also total egal zu sein? Wie kommt das? Ergibt das einen Sinn?

Das andere Problem, wie rufe ich es auf, die Prozedure im Array? Diese 2 Versuche haben jedenfalls nicht geklappt bei mir:

Code: Alles auswählen

ProzeArray[1];
ProzeArray[FMachFormRot];
Was übersehe/vergesse ich?
Ja, das ergibt Sinn. Wie ich schon gesagt habe, man macht hier nichts weiter als ein Zeiger auf eine Prozedur oder Funktion zu definieren. Einziger Unterschied ist bei der Typdeklaration lässt man Namen weg:

Code: Alles auswählen

Type  procedure(FMachFormBlau, FMachFormRot, FMachFormGelb: integer); //ohne Name und Code-Teil

procedure MeineProd(FMachFormBlau, FMachFormRot, FMachFormGelb: integer);
begin
   //... irgend ein code
end;
Für die zweite Frage(Lies Kommentare):

Code: Alles auswählen

program Project1;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}
  cthreads,
  {$ENDIF}
  Classes
  { you can add units after this };

type
  TMeinProc = procedure(AValue1, AValue2: integer);
  TProzeArray= Array[0..1] of TMeinProc;

procedure My1(AValue1, AValue2 : integer);
begin
  writeln(AValue1*AValue2);
end;

procedure My2(AValue1, AValue2: integer);
begin
  writeln(AValue1 div AValue2);
end;

var
  ProzeArray: TProzeArray;
begin
  // Prozeduren zuweisen
  ProzeArray[0]:=@My1;
  ProzeArray[1]:=@My2;

  //Benutzung:
  // Das ist der Zugriff auf die 1. Prozedur: ProzeArray[0] 
  // Das sind die Parameter: (2,3)
  // Es ist wie:  My1(2,3);
  ProzeArray[0](2,3);
  //Zugriff auf die 2.Prozedur
  ProzeArray[1](8,4);
  readln;
end.  
Man kann auch Funktionen mit Rückgabewerte verwenden, es ist keine Hexerei:

Code: Alles auswählen

program Project1;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}
  cthreads,
  {$ENDIF}
  Classes
  { you can add units after this };

type
  TMeinProc = function(AValue1, AValue2: integer):integer;
  TProzeArray= Array[0..1] of TMeinProc;

function My1(AValue1, AValue2 : integer):integer;
begin
  Result:=(AValue1*AValue2);
end;

function My2(AValue1, AValue2 : integer):integer;
begin
  Result:=(AValue1 div AValue2);
end;

var
  ProzeArray: TProzeArray;
begin
  ProzeArray[0]:=@My1;
  ProzeArray[1]:=@My2;

  Writeln(ProzeArray[0](2,3));
  Writeln(ProzeArray[1](8,4));
  readln;
end. 
Ich verwende so etwas um Units von einander unabhängig zu machen. Die ganze LCL arbeitet damit. Normalerweise benutzt man es ohne Array:

Code: Alles auswählen

type
  TMeinProc = function(AValue1, AValue2: integer):integer;
function My1(AValue1, AValue2 : integer):integer;
begin
  Result:=(AValue1*AValue2);
end;

var
  MeinProc: TMeinProc;
begin
  MeinProc:=@My1;

  if Assigned(MeinProc) then Writeln(MeinProc(2,3));
  readln;
end. 
Sinn ergibt das wenn man diese beiden:

Code: Alles auswählen

type
  TMeinProc = function(AValue1, AValue2: integer):integer;
var
  MeinProc: TMeinProc;
in UnitA packt und die Funktion in UnitB. So kann man sein Projekt gut organisieren und auch in Teams ohne DLL's arbeiten.

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

Re: Wie bekomme ich Procedure in Array rein?

Beitrag von kupferstecher »

Erwin hat geschrieben:
Sa 25. Nov 2023, 19:59
Las vorhin im anderem Thread, dass man sogar Procedure in Array rein tun kann? Wahnsinn.
8)

Hallo Erwin,

schau dir erstmal die "Prozeduralen Typen" an, wie sie unter Pascal heißen. Unter c würde man Funktionspointer sagen. Das sind Variablen, denen man eine Funktion zuweisen kann und später die Funktion aufrufen. Dazu muss die Deklaration des Prozeduralen Typen mit der Funktion übereinstimmen.

Z.B. man hat eine Prozedur

Code: Alles auswählen

  Procedure PrintSth(aString: String);
und möchte die in einer Variablen "speichern", dann wird die Variable so deklariert:

Code: Alles auswählen

var
  FktPtr: Procedure(aString: String);
und zugewiesen:
begin

Code: Alles auswählen

  FktPtr:= @PrintSth;
und aufgerufen:

Code: Alles auswählen

  FktPtr("Hallo Erwin");
Das hat jetzt erstmal nichts mit Arrays zu tun, aber man kann diese Prozedurelen Typen natürlich auch als Array organisieren.

https://www.freepascal.org/docs-html/ref/refse17.html

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

Re: Wie bekomme ich Procedure in Array rein?

Beitrag von Mathias »

Aber zum einen verwirrt mich das mit dem Integer, dem Beispiel. Habe statt Integer es mit String versucht. Scheint also total egal zu sein? Wie kommt das? Ergibt das einen Sinn?
Der i war nur aus einer Altlast, aus dem ich das Muster generierte. Wie andere schon sagten, man kann darauf verzichten oder auch andere Typen nehen.
Auch das nop ist Sinnlos, mit ging es nur drum zum feststellen, wie schnell die Aufrufe gehen.
Auch ist es nicht auf proceduren fixiert, auch functionen gehen.


Nachtrag:
Ein typische Klassiker, bei dem man eine Procedure verwendet, ist ExitProc.
https://wiki.freepascal.org/ExitProc/de
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Wie bekomme ich Procedure in Array rein?

Beitrag von Erwin »

kupferstecher, das Deinige ist mir leider etwas zu ... komplex? Mir fällt da auch keine direkte Anwendungsmöglichkeit ein, was es mir noch schwerer macht, es zu verstehen.

Soner, mir scheint wir haben etwas aneinander vorbei geschrieben. Teils kein Wunder, irgendwie stehe/stand ich, was die tieferen Funktion von Array etc. betraf, auf Kriegsfuß. Viele mir bekannte Beispiele zu Array sind zu einfach, dass es mir teils an Fantasie fehlte, dass in einen Fach/Bereich des Array mehr als nur eine Sache rein kann, und dass obwohl ich öfters Array mit Objekten als Inhalt erstellt habe. Letztendlich habe ich den Ganzen Dreh doch noch heraus bekommen. Musste allerdings eben einiges an meine Prozeduren bzw. Typisierung anpassen.

Code: Alles auswählen

...
uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    ...
  private

  public

  end;

  procedure FarbeRot(); // ** 1 **
  procedure FarbeSchwarz();
  procedure FarbeGelb();
  procedure FarbeBlau();
  function ZahlSummieren(Edit1,Edit2:Integer):Integer;

var
  Form1: TForm1;

type
  TFarbProc=procedure(); // ** 2 **
  TZahlFunc=function(Zahl1,Zahl2:Integer):Integer;

var
  MeyRec : record
    Zahl: Integer;
    Wort: String[5];
    Proze: TFarbProc;
    Funki: TZahlFunc;
    end = (Zahl:5; Wort:'Ok'; Proze: @FarbeBlau; Funki: @ZahlSummieren); // ** 3 **

  ProzeFarb:Array[0..2] of TFarbProc;


implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.ProcFuellenBClick(Sender: TObject);
begin
  // Prozeduren zuweisen
  ProzeFarb[0]:=@FarbeRot;
  ProzeFarb[1]:=@FarbeSchwarz;
  ProzeFarb[2]:=@FarbeGelb;
end;

procedure TForm1.ButtonRecAusfuehrenClick(Sender: TObject);
begin
  MeyRec.Proze;
  ShowMessage(IntToStr(MeyRec.Funki(StrToInt(Edit1.Text),StrToInt(Edit2.Text))));
end;

function ZahlSummieren(Edit1,Edit2:Integer):Integer;
begin
  Result:=Edit1+Edit2;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ProzeFarb[0];
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  ProzeFarb[1];
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  ProzeFarb[2];
end;

// Rest eher uninteressant
procedure FarbeRot();
begin
  Form1.Color:=clRed;
end;

procedure FarbeSchwarz();
begin
  Form1.Color:=clBlack;
end;

procedure FarbeGelb();
begin
  Form1.Color:=clYellow;
end;

procedure FarbeBlau();
begin
  Form1.Color:=clAqua;
end;

end.
Folgendes musste ich feststellen. Bei Punkt 1, wo die Prozeduren für die Farben sind, diese müssen leider außerhalb von Form1 (der Klasse?) erstellt werden. Nur so kann man es ohne TForm1 verwenden. Mit TForm1 geht es leider nicht. ??

Zu 2) Jetzt wo ich endlich begriff, was ja eigentlich offensichtlich ist, nämlich dass man der Array mitteilen muss, was so alles bei der Prozedur dabei ist, konnte ich es anpassen. Weil Prozeduren haben oft unterschiedliche Variablen. Und dementsprechend muss dann auch die Typisierung erstellt werden. Was in meinem Fall statt 'procedure(FMachFormBlau, FMachFormRot, FMachFormGelb: integer);', einfach nur 'procedure();' heißen muss. Weil meine Prozeduren ja keine Werte verlangen. Aber das '()' muss scheinbar mit dabei sein.

3) Was ich auch wissen wollte, aber nach dem ich das andere Verstand selbst zustande brachte: Wie man so was auch in Records einbauen kann. Muss man halt mit Zeiger dann machen. Jetzt weiß ich endlich, wozu Zeiger gut sind.

Mathias, danke dass Du Dich zu Wort ... Schrift gemeldet hast. Daraufhin habe ich noch mal in Deinem Tread, der ja mein Interesse angestoßen hat, rein geschaut. Und habe da die eine Frage mit der Direkten Zuweisung dann endlich verstanden. Daraufhin habe in inzwischen mein Code etwas umgeändert. Habe die Typisierung raus geworfen. Und also statt ...

Code: Alles auswählen

  Proze: TFarbProc; // im Recod
  Funki: TZahlFunc; // im Record
    
  ProzeFarb:Array[0..2] of TFarbProc;
gleich so gemacht:

Code: Alles auswählen

  Proze: procedure(); // im Record
  Funki: function(Zahl1,Zahl2:Integer):Integer; // im Record
  ProzeFarb:Array[0..2] of procedure();
Funktionierte ebenfalls wunderbar. Dadurch spare ich mir die Typisierung.
Ich stehe einfach auf Kriegsfuß mit den ... Typen. Liegt einfach auch daran, dass man mir bis jetzt nicht wirklich den Sinn davon erklären konnte. Ja, man kann diese öfters verwenden. Aber in der Regel verwende ich es ja nur einmal. Und da finde ich Umwege oder eben Zwischen-Strecken befremdlich, weil unnötig.

Danke für Eure Hilfe.
Theoretisch wird mir das vieles vereinfachen. ... fragt sich aber wie so oft, ob es auch den Praxistest besteht, oder ich doch mich in was verrannt habe, oder zu Aufwendig ist, etc.
Lazarus 2.2.0 / FP 3.2.4

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

Re: [gelöst] Wie bekomme ich Procedure in Array und Record rein?

Beitrag von Mathias »

Ich habe mal ein recht abgespecktes Beispiel gemacht, da sollte man eher sehen, was passiert.
Spannend ist, wen dur 'r' drückst. Bei den drei Funktionen im Beispiel, könnte man ein einfache case nach Random machen.
Aber stell die mal vor es wären 100 oder mehr Proceduren, dann würde case recht lahm werden.

Code: Alles auswählen

program project1;
uses
  Crt;

  procedure red;
  begin
    WriteLn('rot');
  end;

  procedure green;
  begin
    WriteLn('grün');
  end;

  procedure blue;
  begin
    WriteLn('blau');
  end;

var
  ColorProc: array of procedure = (@red, @green, @blue);
  ch: char;
begin

  repeat
    ch := ReadKey;
    case ch of
      '1': begin
        ColorProc[0];
      end;
      '2': begin
        ColorProc[1];
      end;
      '3': begin
        ColorProc[2];
      end;
      'r': begin
        ColorProc[Random(3)];
      end;
    end;
  until ch = #27;
end.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: [gelöst] Wie bekomme ich Procedure in Array und Record rein?

Beitrag von Erwin »

Verdammt, das habe ich glatt übersehen: Die Probleme beim direkten Zuweisen der Prozedure war ja dass dies nicht direkt sondern nur mit Zeiger geht. Somit ist auch folgendes in meinen Fall möglich:

Code: Alles auswählen

ProzeFarb:Array[0..2] of procedure() = (@FarbeRot, @FarbeSchwarz, @FarbeGelb);
Dadurch erspare ich mir die extra Zuweisung, sondern kann die Prozeduren gleich beim erstellen zuweisen.
Danke.
Lazarus 2.2.0 / FP 3.2.4

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

Re: [gelöst] Wie bekomme ich Procedure in Array und Record rein?

Beitrag von Mathias »

Erwin hat geschrieben:
So 26. Nov 2023, 16:57
Verdammt, das habe ich glatt übersehen: Die Probleme beim direkten Zuweisen der Prozedure war ja dass dies nicht direkt sondern nur mit Zeiger geht. Somit ist auch folgendes in meinen Fall möglich:

Code: Alles auswählen

ProzeFarb:Array[0..2] of procedure() = (@FarbeRot, @FarbeSchwarz, @FarbeGelb);
Dadurch erspare ich mir die extra Zuweisung, sondern kann die Prozeduren gleich beim erstellen zuweisen.
Danke.
Nur habe ich ein dynamisches Array verwendet und du ein statisches.
Bei meiner Varianten kann man noch proceduren zur Laufzeit ergänzen/entfernen.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: [gelöst] Wie bekomme ich Procedure in Array und Record rein?

Beitrag von Erwin »

Mathias hat geschrieben:
So 26. Nov 2023, 17:23
Nur habe ich ein dynamisches Array verwendet und du ein statisches.
Bei meiner Varianten kann man noch proceduren zur Laufzeit ergänzen/entfernen.
Tatsächlich?! Danke. Ich dachte immer, bzw. habe es oft so gelesen, dass man Dynamischen Arrays bei der Erstellung keine Werte zuweisen könne? Werte selbst geht also doch, nur die Länge kann man halt bei der Erstellung nicht festlegen!? Bzw. nur indirekt mit der Zuweisung von Werten, und dann ist die Dynamische Array vorerst dann halt so groß(?)!
Lazarus 2.2.0 / FP 3.2.4

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

Re: [gelöst] Wie bekomme ich Procedure in Array und Record rein?

Beitrag von Mathias »

Ich dachte immer, bzw. habe es oft so gelesen, dass man Dynamischen Arrays bei der Erstellung keine Werte zuweisen könne?
Da hast du recht, dies war mal so, aber unterdessen geht es problemlos.

Du kannst sogar zu Laufzeit neue Element hinzufügen.

Code: Alles auswählen

  procedure rosa;
  begin
    WriteLn('rosa');
  end;
...
  Insert(@rosa, ColorProc, 0);
nur die Länge kann man halt bei der Erstellung nicht festlegen!?
Ist in der Regel auch nicht nötig, ansonsten gibt es setLength().
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten