Direkt zum Inhalt | Direkt zur Navigation

Sektionen
Benutzerspezifische Werkzeuge

Seminar 6 - Teil 2

Rose Trees + Programmsynthese - Hendrik Geßner

rose-trees-synthese.lhs — LHS source code, 4Kb

Dateiinhalt

Helfer: Binärbäume

> data Btree a = Leaf a | Fork (Btree a) (Btree a)
> showBtree (Leaf a) = show a
> showBtree (Fork xt yt) = "("++ (showBtree xt) ++" "++ (showBtree yt) ++")"


-------------------------------------
-                                   -
-  RoseTrees und Programmsynthese   -
-                                   -
-------------------------------------

Das besondere an RoseTrees ist die unbegrenzte Anzahl an Söhnen an jedem Knoten. 
Daraus ergibt sich auch die Definition von "Knoten" und "Blatt": Knoten haben 
eine nicht-leere Liste von Söhnen, Blätter haben eine leere Liste von Söhnen.

> data Rose a = NodeR a [Rose a]
> roseTree = NodeR 1 [NodeR 2 [NodeR 5 []],NodeR 3 [],NodeR 4 []]

Natürlich lassen sich die üblichen Baum-Operationen auch auf RoseTrees definieren.

> foldRose :: (a -> [b] -> b) -> Rose a -> b
> foldRose f (NodeR x xts) = f x (map (foldRose f) xts)

> flattenR :: Rose a -> [a]
> flattenR (NodeR x xts) = x : concat (map flattenR xts)

> size = foldRose f where f x ns = 1 + sum ns

Zwischen Binärbäumen und RoseTrees existiert eine Abbildung, die sogar ein-eindeutig 
ist. Die Abbildung eines RoseTrees in einen Binärbaum ist wie folgt definiert:

> toB :: Rose a -> Btree a
> toB (NodeR x xts) = foldl Fork (Leaf x) (map toB xts)

Die Umkehrfunktion toR ist schwieriger zu implementieren. Anstatt sich selbst eine 
Funktionsdefinition auszudenken kann diese über Programmsynthese gebaut werden.

< toR :: Btree a -> Rose a
< toR (toB xt) = xt

Um eine Funktionsdefinition über Programmsynthese zu erstellen benötigen wir zwei 
Eigenschaften der bereits bekannten Funktion toB:

< toB (Node x []) = Leaf x
< toB (Node x (xts ++ [xt])) = Fork (toB (Node x xts)) (toB xt)

-----------------------
-- Fall 1: Node x [] --
-----------------------

< toR (toB (Node x [])) = Node x []
= { erste Eigenschaft von toB }
< toR (Leaf x) = Node x []

----------------------------------
-- Fall 2: Node x (xts ++ [xt]) --
----------------------------------

< toR (toB (Node x (xts ++ [xt]))) = Node x (xts ++ [xt])
= { zweite Eigenschaft von toB }
< toR (Fork (toB (node x xts)) (toB xt)) = Node x (xts ++ [xt])
= { definiere xb = toB (Node x xts) und yb = toB xt }
< toR (Fork xb yb) = Node x (xts ++ [xt])
< where Node x xts = toR xb
<       xt         = toR yb

Damit ist toR, die Umkehrfunktion von toB, vollständig errechnet worden. Dieses 
Vorgehen heißt Programmsynthese.

Zu Beginn der Programmsynthese haben wir uns auf zwei Eigenschaften von toB 
gestützt. Diese Eigenschaften sind natürlich nicht aus der Luft gegriffen, 
sondern können bewiesen werden. Beide gehen zurück auf die Definition von toB:

< toB :: Rose a -> Btree a
< toB (NodeR x xts) = foldl Fork (Leaf x) (map toB xts)

Für xts werden zwei Fälle unterschieden: Fall 1 (xts ist leer) und Fall 2 
(xts besitzt mindestens ein Element). Fall 1 zuerst.

< toB (Node x []) = foldl Fork (Leaf x) (map toB [])
< = { def. map }
< toB (Node x []) = foldl Fork (Leaf x) []
< = { def. foldl }
< toB (Node x []) = Leaf x

Fall 2 stützt sich auf das dritte duality theorem, das besagt:

< foldr op u xs = foldl (flip op) u (reverse xs)

Angewendet auf toB bedeutet das:

< toB (NodeR x (xts ++ [xt])) = foldl Fork (Leaf x) (map toB (xts ++ [xt]))
< = { duality theorem }
< toB (NodeR x (xts ++ [xt])) = foldr (flip Fork) (Leaf x) (reverse (map toB (xts ++ [xt])))
< = { commutativity of reverse and map }
< toB (NodeR x (xts ++ [xt])) = foldr (flip Fork) (Leaf x) (map toB (reverse (xts ++ [xt])))
< = { solve reverse }
< toB (NodeR x (xts ++ [xt])) = foldr (flip Fork) (Leaf x) (map toB ([xt] ++ reverse xts))
< = { solve map }
< toB (NodeR x (xts ++ [xt])) = foldr (flip Fork) (Leaf x) ([toB xt] ++ map toB (reverse xts))
< = { solve foldr }
< toB (NodeR x (xts ++ [xt])) = toB xt `(flip Fork)` (foldr (flip Fork) (Leaf x) (map toB (reverse xts)))
< = { put (flip Fork) in front }
< toB (NodeR x (xts ++ [xt])) = (flip Fork) (toB xt) (foldr (flip Fork) (Leaf x) (map toB (reverse xts)))
< = { solve flip }
< toB (NodeR x (xts ++ [xt])) = Fork (foldr (flip Fork) (Leaf x) (map toB (reverse xts))) (toB xt)
< = { duality theorem }
< toB (NodeR x (xts ++ [xt])) = Fork (foldl Fork (Leaf x) (map toB xts)) (toB xt)
< = { rev. def. toB }
< toB (NodeR x (xts ++ [xt])) = Fork (toB (NodeR x xts)) (toB xt)

Damit sind die Herleitung beider Fälle gezeigt, die beiden verwendeten Eigenschaften 
von toB bewiesen und somit die Programmsynthese abgeschlossen.

Artikelaktionen
Auf einen Blick
Lehrform Seminar
Empfohlen ab FS 3
Voraussetzungen Grundkenntnisse der Informatik aus den ersten 4 Semestern, vor allen aus den Bereichen Theoretische Informatik und Logik. Teilnahme an der Veranstaltung "Automatisierte Logik und Programmierung" ist vorteilhaft, aber nicht notwendig.
Benotet Ja
Punkte gesamt 3
davon praktisch 3
Sprache deutsch/englisch
Fremdhörer zugelassen? Nein
Teilgebiete Theoretische Informatik(2000), Praktische Informatik(3000), Wahlfrei(7000)
Studiengang Bachelor, Master
Belegung via PULS