04. Übungsblatt - Technische Universität Braunschweig

Werbung
Technische Universität Braunschweig
Institut für Programmierung und Reaktive Systeme
Dr. Werner Struckmann
8. Dezember 2011
Programmieren für Fortgeschrittene
4. Übungsblatt
Das Thema dieses Übungsblatts sind rekursive Datentypen.
Auf diesem Blatt starten wir mit der Implementierung eines Interpreters einer einfachen
Sprache: PCF (Programming Computable Functions). Diese orientiert sich in der Syntax
an üblichen semantischen Definitionen von Programmiersprachen durch Fixpunktterme.
Sie kann als Erweiterung des getypten Lambda-Kalküls gesehen werden.
Die Syntax von PCF ist durch folgende BNF-Grammatik gegeben:
1 e ::= x | n | true | false | succ | pred | iszero | ( e )
2
| if e then e else e
3
| e e
4
| \ x -> e
5
| fix x -> e
Dabei steht x für einen Bezeichner, n für eine natürliche Zahl (mit 0), true und false sind
boolesche Literale. succ, pred sind die üblichen successor und predecessor Funktionen auf
N, während iszero auf Gleichheit mit 0 prüft. Weiterhin können Ausdrücke geklammert
werden. Um Berechnungen zu ermöglichen, existiert das normale if-then-else- Konstrukt
und die Applikation von Funktionen e e. Zuletzt können mit \x -> e und fix x -> e
Variablen eingeführt werden. Dabei entspricht das erste Konstrukt einer Funktion mit einer
Variablen x und einem Ausdruck e, in dem x verwendet werden darf. Das zweite Konstrukt
ist der Fixpunkt-Operator oder Rekursion. Die Verwendung wird später behandelt.
Folgende Regeln werden benutzt, um Ausdrücke eindeutig auswerten zu können:
• Funktionsapplikation ist links-assoziativ, d. h. f g h wird als (f g) h ausgewertet.
• Funktionsapplikation bindet am stärksten, d. h. z. B.
\f -> f 0 wird als \f -> (f 0) ausgewertet.
Zu dieser kontextfreien Grammatik existiert ein Syntaxbaum. Die zugehörigen Datentypen sind die folgenden:
1
2
3
4
5
6
7
8
9
10
type Id = String
data BuiltInFunction = Succ | Pred | IsZero deriving Eq
data Value = NumVal Int | BoolVal Bool deriving Eq
data Term =
Var Id
| Const Value
| BuiltIn BuiltInFunction
11
12
13
14
15
| If Term Term Term
| Lambda Id Term
| App Term Term
| Fix Id Term
deriving Eq
Diese entsprechen der Grammatik.
Für die folgenden Aufgaben werden vorgefertigte Dateien bereitgestellt, die vordefinierte Funktionen und insbesondere vordefinierte Testfälle enthalten. Darin ist auch eine
Cabal-Beschreibung enthalten. Diese kann zum Testen genutzt werden. Dazu müssen in
dem Verzeichnis die folgenden Befehle ausgeführt werden:
1
2
3
cabal configure -- enable - tests
cabal build
cabal test
Ein Aufruf sollte Anfangs zur Ausgabe aller Fehler führen. Am Ende dieses Blattes sollte
lediglich noch die Ausgabe Test suite test-pcf: PASS erscheinen.
Die Implementierung befindet sich in dem Ordner lib/PCF. Die Testfälle finden Sie in
der Datei test/test-pcf.hs. Dort können Sie auch sehen, was erfüllt sein müsste.
Das Arbeitstier der Implementierung ist die Funktion
eval :: Term -> Either PcfError Term,
die einen gegebenen Term auswertet oder einen Fehler liefert. In den folgenden Aufgaben
wird diese Auswertungsfunktion schrittweise implementiert. Die benötigten Regeln werden
hier zur Verfügung gestellt.
Zunächst sollten Sie sich mit dem Typ Either vertraut machen. Dieser ist definiert
durch
1 data Either a b = Left a | Right b
Wenn Either zur Fehlerbehandlung verwendet wird, wird der Fehler per Konvention in
dem Left-Konstruktor hinterlegt, während das Ergebnis in Right liegt.
Aufgabe 10: Schreiben Sie die Funktion natSum von Blatt 3 um, sodass Left statt error
verwendet wird, d.h. die Funktion erhält die Signatur
natSum :: Int -> Either String Int. Schreiben Sie anschließend eine Funktion
natSumList :: [Int] -> Either String Int, die für jeden Eintrag in der gegebenen Liste
natSum aufruft und die Ergebnisse aller Aufrufe addiert. Sie können dafür foldl benutzen.
Im Folgenden werden wir ein Regelsystem angeben, das die Semantik der Sprache
definiert. Wir nutzen dabei eine Art der Darstellung, die als „natural semantics“ bezeichnet
wird (vergleiche Kahn, Natural Semantics). Die Regeln bestehen dabei aus Bewertungen
(„judgements“) der Form e ⇒ v. Dies bedeutet, dass e zum Wert v ausgewertet wird. Die
Regeln werden dabei soweit angewendet, bis keine weitere Auswertung mehr möglich ist
oder eine Auswertung nichts mehr ändert.
–2–
Axiome:
n⇒n
true ⇒ true und f alse ⇒ f alse
succ ⇒ succ, pred ⇒ pred und iszero ⇒ iszero
(1)
(2)
(3)
Regeln:
b ⇒ true
e1 ⇒ v
(4)
if b then e1 else e2 ⇒ v
Oben stehen die Hypothesen und unten die Konklusion. Das heißt. falls b zu true ausgewertet wird und e1 zum Wert v, dann kann der Ausdruck
if b then e1 else e2 zu v ausgewertet werden. Analog für den else-Zweig:
b ⇒ f alse
e2 ⇒ v
if b then e1 else e2 ⇒ v
Die Auswertung der vordefinierten Funktionen ist wie folgt definiert:
e1 ⇒ succ
e2 ⇒ n
e1 e2 ⇒ n + 1
e1 ⇒ pred
e2 ⇒ 0
e1 ⇒ pred
e2 ⇒ n + 1
e1 e2 ⇒ 0
e1 e2 ⇒ n
e1 ⇒ iszero
e2 ⇒ 0
e1 ⇒ iszero
e2 ⇒ n + 1
e1 e2 ⇒ true
e1 e2 ⇒ f alse
Aufgabe 11:
(5)
(6)
(7)
(8)
Implementieren Sie die Regeln (1) bis (8) durch die Funktion
eval :: Term -> Either PcfError Term.
Dabei sollten Sie die Auswertung der Regeln (6) bis (8) in die Funktion
Term -> Term -> Either PcfError Term auslagern.
Für alle anderen Konstrukte kann die vorgegebene Implementierung, die einen Fehler
zurückgibt beibehalten werden. Beachten Sie, dass im Fehlerfall die Auswertung abgebrochen und der Fehler weitergereicht werden soll (siehe vorige Aufgabe).
Danach sollten Sie die Testfälle check1 bis check6 abdecken.
Um eine Funktion definiert durch \x -> e o. Ä. auszuwerten, muss ein Verfahren zur
Übergabe von Parametern definiert werden. Wir wählen hier die textuelle Ersetzung. Wir
werden dabei die Notation e[x := t] verwenden, um die Ersetzung von x durch t in e zu
bezeichnen. Zum Beispiel ergibt (succ x)[x := 1] den Ausdruck succ 1. Hierbei darf in e der
Bezeichner x nur dann ersetzt werden, wenn x frei vorkommt, d. h. nicht durch \x -> oder
fix x -> gebunden ist.
Beispiel: ((\x -> succ x) (pred x))[x := 3] ergibt ((\x -> succ x) (pred 3)).
Aufgabe 12: Implementieren Sie die Funktion subst :: Term -> Id -> Term -> Term,
die in einem Term e, alle freien Vorkommen eines Bezeichners x durch einen Term t ersetzt.
d. h. e[x := t] berechnet. Nach der Implementierung sollten auch die Testfälle check7 und
check8 erfüllt sein.
–3–
Als nächstes wollen wir Lambda-Terme auswerten. Dazu werden diese zunächst wie die
eingebauten Funktionen zu sich selber ausgewertet:
λx → e ⇒ λx → e
(9)
Die eigentliche Berechnung kommt durch die Applikation auf ein Argument zustande.
Wir benutzen call-by-value, d. h. zuerst wird das Argument ausgewertet, danach in dem
Rumpf des Lambda-Ausdrucks ersetzt und dann die Ersetzung ausgewertet.
e1 ⇒ λx → e
e2 ⇒ v1
e1 e2 ⇒ v
e[x := v1 ] ⇒ v
(10)
Beispiel: (\x -> succ x) (succ 0). Hier wird zuerst succ 0 zu 1 ausgewertet. Dann
wird x im Rumpf succ x ersetzt und zu 2 ausgewertet.
Beachten Sie, dass für Bezeichner keine Auswertungsregel existiert. Diese werden lediglich durch die obige Ersetzung belegt. Sollte bei der Auswertung eines Ausdrucks also ein
Bezeichner auftreten, so ist dieser ungebunden und muss zu einem Fehler führen.
Aufgabe 13: Benutzen Sie die Funktion subst, um eval so zu erweitern, dass Funktionsapplikationen nach Regel (10) ausgewertet wird. Dies sollte sollte dafür sorgen, dass Ihre
Implementierung die Testfälle check7b und check8b erfüllt.
Das letzte Konstrukt der Sprache, das noch umgesetzt werden muss, ist die Fixpunktbestimmung bzw. Rekursion. Diese ist interessanterweise sehr einfach:
e[x := (fix x → e)] ⇒ v
(fix x → e) ⇒ v
(11)
Was passiert hier? Wenn wir einen Ausdruck e gegeben haben, dann ersetzen wir das
Vorkommen der Fixpunktvariablen durch den Ausdruck inklusive des Rekursionsschritt.
Hier noch ein Beispiel für ein Fixpunktkonstrukt, an dem man sich die Funktionsweise
klar machen sollte (Berechnung der Summe zweier Zahlen):
1 fix sum -> \ x -> \ y ->
2
if iszero x then y else sum ( pred x ) ( succ y )
Dies entspricht dem Haskellkonstrukt
1 sum x y = if iszero x then y else sum ( x - 1) ( y + 1)
Aufgabe 14: Implementieren Sie abschließend die Fixpunktbestimmung in eval nach
obiger Regel. Ihre Implementierung sollte nun alle Testfälle erfüllen.
–4–
Herunterladen