Ich bin dabei, meine eigene Programmiersprache zu erstellen, die ich zu Lernzwecken verwende. Ich habe bereits das Lexer und ein rekursives Descent-Parser für eine Teilmenge meiner Sprache geschrieben (derzeit unterstütze ich mathematische Ausdrücke wie + - * /
und in Klammern). Der Parser gibt mir einen abstrakten Syntaxbaum zurück, auf dem ich die Evaluate
Methode aufrufe, um das Ergebnis des Ausdrucks zu erhalten. Alles funktioniert gut. Hier ist ungefähr meine aktuelle Situation (Codebeispiele in C #, obwohl dies ziemlich sprachunabhängig ist):
public abstract class Node
{
public abstract Double Evaluate();
}
public class OperationNode : Node
{
public Node Left { get; set; }
private String Operator { get; set; }
private Node Right { get; set; }
public Double Evaluate()
{
if (Operator == "+")
return Left.Evaluate() + Right.Evaluate();
//Same logic for the other operators
}
}
public class NumberNode : Node
{
public Double Value { get; set; }
public Double Evaluate()
{
return Value;
}
}
Ich möchte den Algorithmus jedoch von den Baumknoten entkoppeln, da ich das Open / Closed-Prinzip anwenden möchte, damit ich nicht jede Knotenklasse erneut öffnen muss, wenn ich beispielsweise die Codegenerierung implementieren möchte. Ich habe gelesen, dass das Besuchermuster dafür gut ist. Ich habe ein gutes Verständnis dafür, wie das Muster funktioniert, und die Verwendung von Doppelversand ist der richtige Weg. Aber aufgrund der rekursiven Natur des Baumes bin ich mir nicht sicher, wie ich es angehen soll. So würde mein Besucher aussehen:
public class AstEvaluationVisitor
{
public void VisitOperation(OperationNode node)
{
// Here is where I operate on the operation node.
// How do I implement this method?
// OperationNode has two child nodes, which may have other children
// How do I work the Visitor Pattern around a recursive structure?
// Should I access children nodes here and call their Accept method so they get visited?
// Or should their Accept method be called from their parent's Accept?
}
// Other Visit implementation by Node type
}
Das ist also mein Problem. Ich möchte es sofort angehen, während meine Sprache nicht viele Funktionen unterstützt, um später ein größeres Problem zu vermeiden.
Ich habe dies nicht in StackOverflow gepostet, weil ich nicht möchte, dass Sie eine Implementierung bereitstellen. Ich möchte nur, dass Sie Ideen und Konzepte mitteilen, die ich möglicherweise übersehen habe und wie ich damit umgehen sollte.
quelle
Antworten:
Es liegt an der Besucherimplementierung, zu entscheiden, ob und in welcher Reihenfolge untergeordnete Knoten besucht werden sollen. Das ist der springende Punkt des Besuchermusters.
Um den Besucher an weitere Situationen anzupassen, ist es hilfreich (und durchaus üblich), solche Generika zu verwenden (es ist Java):
Und eine
accept
Methode würde so aussehen:Dadurch können zusätzliche Parameter an den Besucher übergeben und ein Ergebnis daraus abgerufen werden. Die Ausdrucksauswertung kann also folgendermaßen implementiert werden:
Der
accept
method-Parameter wird im obigen Beispiel nicht verwendet, aber glauben Sie mir: Es ist sehr nützlich, einen zu haben. Beispielsweise kann es sich um eine Logger-Instanz handeln, an die Fehler gemeldet werden.quelle
Ich habe das Besuchermuster zuvor in einem rekursiven Baum implementiert.
Meine spezielle rekursive Datenstruktur war extrem einfach - nur drei Knotentypen: der generische Knoten, ein interner Knoten mit untergeordneten Knoten und ein Blattknoten mit Daten. Dies ist viel einfacher als ich es von Ihrem AST erwarte, aber vielleicht können die Ideen skalieren.
In meinem Fall habe ich absichtlich nicht zugelassen, dass die Annahme eines Knotens mit Kindern die Annahme für seine Kinder aufruft oder den Besucher aufruft. Besuchen Sie (Kind) innerhalb der Annahme. Es liegt in der Verantwortung der korrekten Implementierung des Besuchers als "Besuch" -Mitglied, die Annahme an Kinder des besuchten Knotens zu delegieren. Ich habe mich für diesen Weg entschieden, weil ich verschiedenen Visitor-Implementierungen erlauben wollte, die Reihenfolge der Besuche unabhängig von der Baumdarstellung zu bestimmen.
Ein zweiter Vorteil ist, dass in meinen Baumknoten fast keine Artefakte des Besuchermusters vorhanden sind - jedes "Annehmen" ruft beim Besucher nur "Besuch" mit dem richtigen konkreten Typ auf. Dies macht es einfacher, die Besuchslogik zu lokalisieren und zu verstehen, alles ist in der Besucherimplementierung enthalten.
Der Klarheit halber habe ich einen C ++ - ischen Pseudocode hinzugefügt. Zuerst die Knoten:
Und der Besucher:
quelle
allow different Visitor implementations to be able to decide the order of visitation
. Sehr gute Idee.Sie arbeiten das Besuchermuster um eine rekursive Struktur herum, wie Sie es auch mit Ihrer rekursiven Struktur tun würden: indem Sie die Knoten in Ihrer Struktur rekursiv besuchen.
quelle