Wie mache ich die Traverse-Funktion richtig?

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
alfware17
Beiträge: 214
Registriert: Di 14. Dez 2010, 23:27

Wie mache ich die Traverse-Funktion richtig?

Beitrag von alfware17 »

Ich habe in meinem Programm 4x die fast gleiche Schleife

Code: Alles auswählen

PROCEDURE TMenueManager.Inhalt(Zeiger: PMEintrag; VAR Eintrag: TEintrag);
{$IFDEF DOS} VAR Temp: PEintrag; {$ENDIF}
BEGIN
   {$IFDEF DOS} Temp := Zeiger^.Data; Eintrag := Temp^; {$ELSE} Eintrag := Zeiger^.Data; {$ENDIF}
END;

FUNCTION TMenueManager.FindeMenuepunkt(M1, M2, M3: INTEGER): BOOLEAN;
VAR Zeiger: PMEintrag;
    Eintrag: TEintrag;
    ok: BOOLEAN;
BEGIN
   ok := False;
   Zeiger := Gesamt.Head;
   WHILE (Zeiger <> NIL) AND NOT ok DO BEGIN
      Inhalt(Zeiger, Eintrag);     
      IF (Eintrag.M1 = M1) AND (Eintrag.M2 = M2) AND (Eintrag.M3 = M3) THEN BEGIN
         Punkt := Eintrag; ok := True;
      END;
      Zeiger := Zeiger^.Next;
   END;
   FindeMenuepunkt := ok;
END;
Was mich ärgert, da ich die Inhalt-Procedure vielleicht integrieren und die 3 lokalen Variablen eventuell noch als Objektvariablen machen will, muß aber nicht sein weil gefährlich. Aber ich wollte die redundante Schleife zentral haben.

Mein Versuch mittels

Code: Alles auswählen

 TMenueManager = OBJECT
      Gesamt, Auswahl: TMListe;
      Aktuell, Punkt: TEintrag;
      MenueStapel: {$IFDEF DOS} TStack {$ELSE} specialize TStack<TEintrag> {$ENDIF};
      Angezeigt: BOOLEAN;
      PROCEDURE Init;
      PROCEDURE Freigeben;
      PROCEDURE LoadCSV(CONST FileName: STRING);
      PROCEDURE ZeigeMenue;
      FUNCTION BearbeiteAuswahl: BOOLEAN;
      FUNCTION FindeMenuepunkt(M1, M2, M3: INTEGER): BOOLEAN;
      FUNCTION HatNachfolge(N1, N2: INTEGER): BOOLEAN;
      FUNCTION AktuelleMenuepunkte: BOOLEAN;
      PROCEDURE MenueAusfuehren;

   PRIVATE
      TYPE TFuncEintrag = FUNCTION(VAR Entry: TEintrag): BOOLEAN;
      PROCEDURE Traverse(List: TMListe; Func: TFuncEintrag);
      PROCEDURE Inhalt(Zeiger: PMEintrag; VAR Eintrag: TEintrag);
      FUNCTION FindeMenuepunktFunc(VAR Entry: TEintrag): BOOLEAN;
      FUNCTION HatNachfolgeFunc(VAR Entry: TEintrag): BOOLEAN;
      FUNCTION AktuelleMenuepunkteFunc(VAR Entry: TEintrag): BOOLEAN;
      FUNCTION ZeigeMenueFunc(VAR Entry: TEintrag): BOOLEAN;
   END;



PROCEDURE Init_Eintrag(VAR eintrag: TEintrag);
BEGIN
   WITH Eintrag DO BEGIN
      M1 := 0; M2 := 0; M3 := 0; N1 := 0; N2 := 0; L1 := 0;
      PROG := ''; CODE := ''; BEZEICHNER := '';
   END;
END;

PROCEDURE TMenueManager.Inhalt(Zeiger: PMEintrag; VAR Eintrag: TEintrag);
{$IFDEF DOS} VAR Temp: PEintrag; {$ENDIF}
BEGIN
   {$IFDEF DOS} Temp := Zeiger^.Data; Eintrag := Temp^; {$ELSE} Eintrag := Zeiger^.Data; {$ENDIF}
END;

PROCEDURE TMenueManager.Traverse(List: TMListe; Func: TFuncEintrag);
VAR
   Zeiger: PMEintrag;
   Eintrag: TEintrag;
BEGIN
   Zeiger := List.Head;
   WHILE Zeiger <> NIL DO BEGIN
      Inhalt(Zeiger, Eintrag);
      IF Func(Eintrag) THEN Break;
      Zeiger := Zeiger^.Next;
   END;
END;

FUNCTION TMenueManager.FindeMenuepunktFunc(VAR Entry: TEintrag): BOOLEAN;
BEGIN
   FindeMenuepunktFunc := (Entry.M1 = Aktuell.M1) AND (Entry.M2 = Aktuell.M2) AND (Entry.M3 = Aktuell.M3);
   IF FindeMenuepunktFunc THEN Punkt := Entry;
END;

FUNCTION TMenueManager.FindeMenuepunkt(M1, M2, M3: INTEGER): BOOLEAN;
BEGIN
   Aktuell.M1 := M1; Aktuell.M2 := M2; Aktuell.M3 := M3;
   Traverse(Gesamt, @FindeMenuepunktFunc);
   FindeMenuepunkt := Punkt.M1 = M1; //??? muß noch untersuchen
END;

bringt leider einen Syntaxfehler, sinngemäß Boolean of Object ungleich Boolean.

Warum? Und wie kann ich das lösen. Abgesehen davon, dass es zwar eleganter aussehen könnte aber deutlich mehr Code ist.
Dateianhänge
freiburg-traverse.zip
(3.68 KiB) 148-mal heruntergeladen

Warf
Beiträge: 2150
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Wie mache ich die Traverse-Funktion richtig?

Beitrag von Warf »

Naja ganz einfach weil FindeMenuepunktFunc eine Methode und keine Funktion ist. Als Methode hat es einen self parameter der auf das aktuelle Objekt zeigt. Der muss natürlich auch mit übergeben werden, sonst weiß die Funktion ja nicht was "Aktuell" ist.

Um aus dem Funktionspointer einen Methodenpointer zu machen musst du einfach nur die Definition anpassen:

Code: Alles auswählen

TYPE TFuncEintrag = FUNCTION(VAR Entry: TEintrag): BOOLEAN OF OBJECT;
Und noch Frage am Rande, warum ist entry ein var Parameter, wenn er nicht geändert wird?
Und wo ich Grad dabei bin, warum ist Inhalt eine Methode auf dem MenuManager wenn sie keine der internen Felder anfasst? Warum nicht als Methode von TMEntry?

Code: Alles auswählen

function TMEntry.Inhalt: TEintrag;
begin
  Inhalt := Data{$IfDef DOS}^{$EndIf}
end;

alfware17
Beiträge: 214
Registriert: Di 14. Dez 2010, 23:27

Re: Wie mache ich die Traverse-Funktion richtig?

Beitrag von alfware17 »

Vielen Dank ja das war es. Nun läuft das Programm.
Allerdings habe ich das mit dem Traverse wieder raus genommen aus dem Programm, weil irgendwie sieht es noch komischer aus als wenn ich einfach die while Schleifen lasse. Außerdem wären es damit ziemlich mehr Codezeilen also kontraproduktiv. Und was noch schlimmer ist, meine Bedingungen in den aufrufenden Funktionen sind scheinbar nun falsch denn es kommen nicht die erwarteten Ergebnisse.
Dagegen habe ich scheinbar erfolgreich damit experimentiert, die Funktionen in Prozeduren umzuwandeln (und Schalter beim Objekt angesiedelt zu setzen) und Zeiger sowie Eintrag ebenfalls als Objekt-Variablen, letzteres aber zu 50% rückgängig gemacht. Zwar ist Zeiger jeweils frei und die Funktionen unabhängig, aber das ist nur Glück und sie könnten ja verschachtelt sein und so sparen am falschen Ende.

Deine Frage bezüglich des Ortes von Inhalt. Ja leider geht deine Version nicht, weil Turbo Pascal bei den Funktionsrückgabetypen nicht alles zulässt. Wegen Turbo habe ich die Funktion ja nur gemacht weil da ist der Stacktyp Pointer und nicht Eintrag

Antworten