Seite wählen

Der Metazirkulare Evaluator

von | Sep 11, 2014 | Team

Hallo, ich bin der neue hier. Und als neuer habe ich erstmal nicht das know how um sinnvolle Infos über firmenrelevante Dinge zu verbreiten. Daher kommt jetzt sinnfreie und absolut unrelevante Information mit der ich mich in meiner Freizeit etwas beschäfftigt habe, und zwarin einer Qualität über die ich hoffentlich in paar Jahren lachen kann.
Programmierung wäre langweilig wenn sie immer simpel und leicht verständlich wäre, deswegen ist wohl auch die Dokumentation vieler Programme so schlecht. Und deswegen erfreut sich das Buch Structure and Interpretation of Computer Progams so großer Beliebtheit. Im folgenden möchte ich mich oberflächlich mit dem Kapitel zum Metazirkularen Evaluator beschäftigen. Dieses Kapitel eignet sich sehr gut um sich an den Interpreterbau heran zu führen doch leidet es sehr unter schweren Verständlichkeit der gewählten Programmiersprache und dem geforderten Vorwissen aus früheren Kapiteln. Daher versuche ich hier möglichst viel weg zu lassen und nur das Nötigste an Fällen und Typen zu schildern.
Trotz seines beeindruckenden Namens ist das Prinzip des Metazirkulare Evaluators simpel, er besteht aus eval, dem Auflösen von Ausdrücken, und apply, dem Auflösen von und Ausführen von Prozeduren (Funktionen ohne Rückgabe).
eval erhält einen Ausdruck und dessen Umgebung (lese: Sichtbarkeit/scope) in der Form eval(Ausdruck, Umgebung), je nach Typ des Ausdrucks wird dann vorgegangen. Hier behandle ich nur die Grundlegenden Fälle, im Buch finden sich weitere und es können auch beliebig viele hinzugefügt werden, diese verhalten sich dann analog. In folgenden sind fünf dieser Möglickeiten für eval:

    1. Der Ausdruck ist ein primitiver Wert, etwa eine Zahl oder ein String, dann wird dieser einfach so zurück gegeben.
      eval(5, foo) --> 5
      eval("Baum", foo) --> Baum
    2. Der Ausdruck ist eine Zuweisung oder Definition, entsprechen werden diese dann gesetzt.
      eval((c = 20), foo) --> c in foo = 20
      eval((define add(arg1, arg2): arg1 = arg1 + arg2), foo) -->
      Erstellt Funktion in bar die ein Argument addiert auf ein anderes addiert
    3. Der Ausdruck ist eine Variable, dann wird der entsprechende Wert wert zurück gegeben.
      eval(c, foo) --> 20
    4. Der Ausdruck ist eine Sequenz, also ein Befehl , dieser wird dann Operation für Operation evaluiert
      eval(arg1 = arg1 + arg2, foo) ---> arg = wird als Zuweisung erkannt,
      arg1+arg2 ist eine Prozedur

      eval(arg1 = eval(arg1 + arg2, foo), foo) ---> rekursiv weiter aufgerufen
      eval(arg1 = apply(+, eval(arg1, foo), eval(arg2, foo))) -->
      Bis nur noch primitive Ausdrücke stehen
    5. Der Ausdruck ist ein Prozeduraufruf, dann wird apply für diese Prozedur aufgerufen. Dabei muss eval auf den Operator und auf jedes Argument aufgerufen werden.
      eval(add(c, 5), foo) --> apply(eval(add foo), eval(c, foo), eval(5, foo))
    6. Fehlerfall, irgendwas stimmt nicht. Dafür braucht es nun aber kein Beispiel.

apply hingegen nimmt eine Prozedur und eine Liste von Argumenten eine entgegen gibt und es auch nur drei Fälle:

    1. Die Prozedur ist primitiv und sie wird auf die Argumente angewandt.
      apply(+, 20, 5) --> 20 +5 --> 25
    2. Die Prozedur ist komplex, dann wird eval auf alle Argumente und den Methodenrumpf aufgerufen.
      apply(eval(add, foo), eval(c, foo), eval(5, foo)) -->
      apply(eval(Rumpf von add, foo), 20, 5) -->
      Hierbei wird die Umgebung von add, also foo so erweitert das 20 und 5 zu jeweils arg1 und arg2 zugewiesen werden
    3. Fehlerfall

Dieses eval und apply sind also ineinander verschränkt und rufen sich immer weiter gegenseitig auf bis der Aufruf auf elementare Operationen und Werte reduziert wurde, etwa:
add(c, 5) =
eval(apply(eval(eval(arg1 = apply(eval(arg1, foo), eval(arg2, foo))),
eval(c, foo), 5)), foo) = 25

Einfach, klar und verständlich eben.

0 Kommentare

Einen Kommentar abschicken

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Mehr Beiträge zum Thema Team