Konzepte von Programmiersprachen WS 03/04

Werbung
Konzepte von Programmiersprachen WS 03/04
http://www-pu.informatik.uni-tuebingen.de/users/klaeren/kvps
H. Klaeren,
H. Gast
B LATT 2
Abgabe: 30.10.2003, 9:30h
(Seite 1 / 2)
Hinweis: Denke auch daran, wie bei Blatt 1 ein Makefile mit einem all: zu schreiben. Die Abgabe
zu diesem Blatt ist eine Datei ueb02.zip, die ein Unterverzeichnis ueb02 mit allen Dateien der
Lösungen enthält. Aufgaben 1, 2 sind auf Papier in der Vorlesung abzugeben.
Hinweis: Es steht eine Datei rahmen02.zip bereit, die schon einige Hilfen enthält; inbesondere
liegt in rahmen/html eine HTML-Dokumentation zum unten benötigten Parser arith_rd. Die noch
fehlenden Links in index.html werden in den nächsten Wochen ausgefüllt. Nach dem Auspacken
mit unzip muß noch in ueb02 ein make -C rahmen all ausgeführt werden.
Aufgabe 1 [4] Syntax, Pragmatik, Semantik
1. Gib zwei Beispiele für syntaktisch richtige OCaml-Programme, die dennoch nicht übersetzbar
sind; gibt die Fehlermeldung des OCaml-Compilers an. Die Fehlermeldungen der Beispiele
sollen sich nicht ähneln.
2. Gib ein Beispiel für einen OCaml-Ausdruck, der ein wohldefiniertes Ergebnis liefern würde,
der aber trotzdem nicht übersetzt wird.
3. Inwiefern haben 42 und ((fun _ -> 42) 8) dieselbe Bedeutung?
4. Beim Programmieren muß man viele Entscheidungen treffen. Gib je zwei Beispiele (in Deiner
Lieblingsprogrammiersprache, solange sie mehr als 5 Benutzer hat) für Entscheidungen, die in
die Kategorien Syntax, Semantik und Pragmatik fallen.
Aufgabe 2 [4] Interpretation vs. Compilation
• Beschreibe umgangssprachlich, welche Einzelschritte (in welcher Reihenfolge) ausgeführt werden müssen, um den Ausdruck (Operatoren schon durchnumeriert)
4 +1 (3 +2 x) ∗3 (3 +4 x)
auszurechnen. Dabei soll streng von links nach rechts und von innen nach außen ausgewertet
werden und außerdem müssen Konstanten zu sich selbst ausgewertet werden. Benenne alle
Zwischenergebnisse; als Operationen sind nur +, −, ∗ auf Zwischenergebnissen und Variablen
zugelassen.
• Wie würde ein Compiler bzw. ein Interpreter mit dieser Schrittfolge umgehen?
• Welche Optimierung könnte ein Compiler einbauen?
• Wo zeigt sich die semantische Lücke in diesem Beispiel?
• Ist die Perl-Implementierung in den Pools auf der MS compiler- oder interpreter-basiert? Finde
Hinweise in man perl!
Aufgabe 3 [10] Fingerübungen für Funktionen
Funktionen sind in OCaml ganz normale Ausdrücke – daran muß man sich zunächst gewöhnen. Löse
in einer Datei func.ml die folgenden Aufgaben (denke auch an die Kommentare).
• Für Funktionen f , g ist die Komposition g ◦ f (lies: g nach f ) definiert durch: (g ◦ f )(x) :=
g( f (x)). Schreibe eine OCaml-Funktion comp, so daß comp g f = g ◦ f . Schreibe die letDefinition zunächst ohne Typ und finde diesen per -i-Option an ocamlc heraus.
• Interpretiere den Typ in Hinblick auf die Bedeutung von comp. (⇒ Kommentar)
Konzepte von Programmiersprachen WS 03/04
http://www-pu.informatik.uni-tuebingen.de/users/klaeren/kvps
H. Klaeren,
H. Gast
B LATT 2
Abgabe: 30.10.2003, 9:30h
(Seite 2 / 2)
• List.map f l wendet f auf jedes Element der Liste l an. Schreibe eine Funktion compmap g f l,
die comp f g auf jedes Element von l anwendet. Finde den Typ wie vorher.
• Schreibe nun eine Funktion compmap’ g f l, die f und g einzeln per List.map anwendet, doch
mittels comp das gleiche Resultat erzielt wie compmap; benutze denselben Typ wie bei compmap.
Beweise, daß die beiden Versionen von compmap das gleiche Resultat haben (im Kommentar;
schreibe ◦ als *).
• Schreibe eine Funktion bind2 f y, die bei einer n ≥ 2 stelligen Funktion f das zweite Argument
als y festhält, die anderen frei läßt.
• Warum ist eine analoge Funktion bind1 unnütz?
Aufgabe 4 [8] Abstrakte Syntaxbäume*
Es soll ein Programm eval geschrieben werden, das beim Aufruf eval e.txt aus der Datei e.txt
einen arithmetischen Ausdruck liest und unter einer festen Variablenbelegung auswertet. Der Ausdruck selbst und das Ergebnis werden ausgegeben. Dies geschieht in zwei Schritten.
(1)
rahmen/syntax enthält einen vollständigen Parser für arithmetische Ausdrücke (i eine Zahl):
e ::= x | i | (e) | e1 + e2 | e1 − e2 | e1 ∗ e2
Die folgende Funktion öffnet die Datei, die im ersten Kommandozeilenargument angegeben ist und
liest daraus einen arithmetischen Ausdruck:
Arith_rd.read_first_arg : unit -> pexp
Ihr Ergebnis ist eine interne Repräsentation des Ausdruck vom Typ pexp (der sog. abstract syntax
tree, definiert in arith_ast.mli). Schreibe also zunächst ein Hauptprogramm, das die obige Funktion aufruft und das Ergebnis sofort mit print_pexp wieder ausgibt; Tipp: open Arith_ast. Dann
kommt ein print_newline(). Es empfiehlt sich, mit let ast= das Ergebnis auch festzuhalten.
Hinweis: Compilieren mit ocamlc -I rahmen/syntax -o eval arith.cma eval.ml
(2) Schreibe eine Funktion let rec eval : pexp -> (pid * int) list -> int, deren zweiter
Parameter eine Assoziationsliste mit einer Variablenbelegung ist. Die Datei arith_ast.mli enthält
die Fallunterscheidung für pexp. Die Variablen (Typ pid, Fall Pexp_id) werden mit List.assoc
auf der Variablenbelegung behandelt, ansonsten geschieht die Interpretation analog zu Blatt 1, Funktion wert. Anstelle der einzelnen Konstruktoren Plus, Minus, Mal hat pexp einen einzigen Konstruktor Pexp_apply of pid * pexp list, dessen erstes Argument der Operator ist. Tipp: Überprüfe
mit match auch gleich, daß genau 2 Operanden übergeben wurden, indem das zweite Argument von
Pexp_apply eine Liste der Länge 2 ist. Wenn dieser Test schiefgeht, wirfst Du eine Ausnahme Arity.
Für die Unterscheidung der Operatoren hilft eine weitereAssoziationsliste, die jedem Operator seine
Auswertungsfunktion zuordnet: let operators : (pid * (int -> int -> int)) list. Sie
enthält also Einträge zu Pid_id("+") usw. Tipp: Man kann + als einzelnen Operator so schreiben
( + ). Vorsicht beim *: Dort sind die Leerzeichen wichtig.
Schließlich wird eval im Hauptprogramm nach der Ausgabe des Ausdrucks mit der folgenden Variablenbelegung aufgerufen:
h Variablenbelegung 2 i ≡
[ (Pid_id("i"), 3); (Pid_id("j"), 5); (Pid_id("k"), 7) ]
Macro never referenced.
Herunterladen