Beispiellösung zum Übungsblatt aus Übung 7

Werbung
Fachrichtung 6.2 — Informatik
Universität des Saarlandes
Tutorenteam der Vorlesung Programmierung 1
Programmierung 1 (Wintersemester 2015/16)
Lösungsblatt: Aufgaben für die Übungsgruppen: 7
(Kapitel 7)
Hinweis: Dieses Übungsblatt enthält von den Tutoren für die Übungsgruppe erstellte Aufgaben.
Die Aufgaben und die damit abgedeckten Themenbereiche sind für die Klausur weder relevant noch irrelevant.
1
Baumgrundschule
Aufgabe 7.1 (Linearisierungen schreiben)
Geben Sie die Prä- und Postlinearisierung des folgenden Baumes an:
Lösung 7.1:
Prälinearisierung: [3, 1, 1, 1, 0, 5, 0, 0, 2, 0, 0, 0, 2, 0, 1, 2, 0, 1, 0, 2, 0, 0]
Postlinearisierung: [0, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 2, 1, 2, 5, 0, 0, 2, 3]
Aufgabe 7.2 (Linearisierungen verstehen)
Betrachten Sie diese Prälinearisierung:
[4, 1, 2, 0, 1, 0, 2, 1, 3, 1, 0, 1, 0, 0, 0, 2, 0, 0, 1, 3, 1, 1, 0, 0, 0]
Versuchen Sie, die folgenden Aufgaben zu lösen, ohne den Baum zu zeichnen.
Wie viele Knoten hat der Baum? Wie viele davon sind innere Knoten, wie viele sind Blätter? Wie ist sein Grad?
Können Sie auch die Tiefe des Baum an seiner Prälinearisierung erkennen?
Lösung 7.2:
Der Baum hat die Größe 25. Er hat 14 innere Knoten und 11 Blätter. Sein Grad ist 4. Die Tiefe ist 5.
Hier ist eine grafische Darstellung des Baumes:
1
Aufgabe 7.3
Schreiben Sie eine Prozedur unterbaum: tree → tree → bool, die zu einem Baum t und einem Baum t0
testet, ob t0 ein Unterbaum von t ist.
Lösung 7.3:
fun unterbaum ( T ts ) t ’ = List . exists ( fn x ⇒ t ’ = x ) ts
2
Baumgymnasium
Aufgabe 7.4 (Elementare Baumprozeduren)
Betrachten Sie die folgenden Prozeduren. Erstellen Sie jeweils ein Ausführungsprotokoll zu dem passenden unten
angegebenen Baum. Machen Sie sich daran klar, wie die Prozeduren funktionieren.
(a) fun size ( T []) = 1
| size ( T ts ) = foldl op+ 1 ( map size ts )
(b) fun depth ( T ts ) = 1 + foldl Int . max ∼1 ( map depth ts )
(c) fun breadth ( T []) = 1
| breadth ( T ts ) = foldl op+ 0 ( map breadth ts )
(d) Überlegen Sie sich eine allgemeine Vorgehensweise, wie man an Baumprozeduren mittels foldl bzw. foldr
und map herangehen kann.
(a)
(b)
(c)
Lösung 7.4:
(a) size ( T [ T [] , T [ T [] , T []]])
= foldl op+ 1 ( map size [ T [] , T [ T [] , T []]])
= foldl op+ 1 [ size ( T []) , size ( T [ T [] , T []])]
= foldl op+ 1 [1 , foldl op+ 1 ( map size [ T [] , T []])]
= foldl op+ 1 [1 , foldl op+ 1 [ size ( T []) , size ( T [])]]
= foldl op+ 1 [1 , foldl op+ 1 [1 ,1]]
= foldl op+ 1 [1 , 3]
= 5
(b) depth ( T [ T [ T []] , T []])
= 1 + foldl Int . max ∼1 ( map depth [ T [ T []] , T []])
= 1 + foldl Int . max ∼1 [ depth ( T [ T []]) , depth ( T [])]
= 1 + foldl Int . max ∼1 [1 + foldl Int . max ∼1 ( map depth [ T []]) ,
2
1 + foldl Int . max ( map depth [])]
= 1 + fold Int . max ∼1 [1 + foldl Int . max ∼1 [ depth ( T [])] ,
1 + foldl Int . max ∼1 []]
= 1 + foldl Int . max ∼1 [1 + foldl Int . max ∼1 [1 + foldl Int . max ∼1
( map depth [])] , 1 + ∼1]
= 1 + foldl Int . max ∼1 [1 + foldl Int . max ∼1 [1 + foldl Int . max ∼1 []] , 0]
= 1 + foldl Int . max ∼1 [1 + foldl Int . max ∼1 [1 + ∼1] , 0]
= 1 + foldl Int . max ∼1 [1 + foldl Int . max ∼1 [0] ,0]
= 1 + foldl Int . max ∼1 [1+0 ,0]
= 1 + foldl Int . max ∼1 [1 ,0]
= 1 + 1
= 2
(c) breadth ( T [ T [] , T [ T [] , T []]])
= foldl op+ 0 ( map breadth [ T [] , T [ T [] , T []]])
= foldl op+ 0 [ breadth ( T []) , breadth ( T [ T [] , T []])]
= foldl op+ 0 [1 , foldl op+ 0 ( map breadth [ T [] , T []])]
= foldl op+ 0 [1 , foldl op+ 1 [ breadth ( T []) , breadth ( T [])]]
= foldl op+ 0 [1 , foldl op+ 0 [1 ,1]]
= foldl op+ 0 [1 , 2]
= 3
Aufgabe 7.5 (Elementare Baumprozeduren – jetzt mit fold)
Betrachten Sie die folgenden Prozeduren, die mit Faltung auf Bäumen arbeiten, und erstellen Sie ein Ausführungsprotokoll, jeweils zum passenden Baum aus Aufgabe 4. Machen Sie sich daran klar, wie die Prozeduren
funktionieren.
(a) fun size ( T ts ) = fold ( fn xs ⇒ foldl op+ 1 xs ) ( T ts )
(b) fun depth ( T ts ) = fold ( fn xs ⇒ 1 + foldl Int . max ∼1 xs ) ( T ts )
(c) fun breadth ( T ts ) = fold ( fn nil ⇒ 1 | xs ⇒ foldl op+ 0 xs ) ( T ts )
(d) Überlegen Sie sich eine allgemeine Vorgehensweise, wie man an Baumprozeduren mittels fold herangehen
kann.
Lösung 7.5:
(a) Sei f := (fn xs ⇒ foldl op+ 1 xs)
size ( T [ T [] , T [ T [] , T []]])
= fold f ( T [ T [] , T [ T [] , T []]])
= f ( map ( fold f ) [ T [] , T [ T [] , T []]])
= f [ fold f ( T []) , fold f ( T [ T [] , T []])]
= f [ f ( map ( fold f ) []) , f ( map ( fold f ) [ T [] , T []])]
= f [ f [] , f [ fold f ( T []) , fold f ( T [])]]
= f [1 , f [ f ( map ( fold f ) []) , f ( map ( fold f ) [])]]
= f [1 , f [ f [] , f []]]
= f [1 , f [1 ,1]]
= f [1 , 3]
= 5
(b) Sei f := (fn xs ⇒ 1 + foldl Int.max ∼1 xs)
depth ( T [ T [ T []] , T []])
= fold f ( T [ T [ T []] , T []])
= f ( map ( fold f ) [ T [ T []] , T []])
= f [ fold f ( T [ T []]) , fold f ( T [])]
= f [ f ( map ( fold f ) [ T []]) , f ( map ( fold f ) [])]
= f [ f [ fold f ( T []) , f []]]
= f [ f [ f ( map ( fold f ) []) , 1 + ∼1]]
= f [ f [ f [] , 0]]
3
= f [ f [0 ,0]]
= f [1]
= 2
(c) Sei f:= (fn nil ⇒ 1 fn xs => foldl op+ 0 xs)|
breadth ( T [ T [] , T [ T [] , T []]])
= fold f ( T [ T [] , T [ T [] , T []]])
= f ( map ( fold f ) [ T [] , T [ T [] , T []]])
= f [ fold f ( T []) , fold f ( T [ T [] , T []])]
= f [ f ( map fold f nil ) , fold f ( T [ T [] , T []])]
= f [ f nil , fold f ( T [ T [] , T []])]
= f [1 , fold f ( T [ T [] , T []])]
= f [1 , f ( map ( fold f ) [ T [] , T []])]
= f [1 , f [ fold f ( T []) , fold f ( T [])]]
= f [1 , f [ f ( map ( fold f ) nil , fold f ( T [])]]
= f [1 , f [ f nil , fold f ( T [])]]
= f [1 , f [1 , fold f ( T [])]]
= f [1 , f [1 , f ( map ( fold f ) nil )]]
= f [1 , f [1 , f nil ]]
= f [1 , f [1 , 1]]
= f [1 , 2]
= 3
Aufgabe 7.6
Schreiben Sie eine Prozedur ternary: tree → tree, die testet, ob ein Baum ternär ist. Ist dies der Fall, so
soll der Baum selbst ausgegeben werden, andernfalls soll die Ausnahme Notter geworfen werden.
Lösung 7.6:
exception Notter
fun ternary ’ ( T [])
| ternary ’ ( T [ t1 , t2 , t3 ])
ternary ’ t1 andalso
| ternary ’
_
= true
=
ternary ’ t2 andalso ternary ’ t3
= false
fun ternary t = if ternary ’ t then t else raise Notter
Alternative:
fun ternary ( T [])
= T []
| ternary ( T [ t1 , t2 , t3 ]) = T [ ternary t1 , ternary t2 , ternary t3 ]
| ternary
_
= raise exception Notter
Aufgabe 7.7
Schreiben Sie eine Prozedur fraktal: tree → tree, die die Blätter eines Baumes durch eine Kopie des
Eingabebaums ersetzt.
Lösung 7.7:
fun fraktal ’ t ( T nil ) = t
| fraktal ’ t ( T ts ) = T ( map ( fraktal ’ t ) ts )
fun fraktal t = fraktal ’ t t
Alternative:
fun fraktal t = fold ( fn nil ⇒ t | ts ⇒ T ts ) t
4
Aufgabe 7.8 (Waldbrand)
(a) Der Baum brennt. Nach dem Feuer sind alle Blätter des Baumes verschwunden, nur noch die verkohlten
Äste sind übrig. Schreiben Sie eine Prozedur feuer: tree → tree, die alle Blätter eines Baumes entfernt.
Lediglich der atomare Baum darf unverändert bleiben.
Beispiel:
•
•
•
Aus dem Baum
•
•
•
wird
• .
(b) Ist es möglich, dass der Baum nach einer Anwendung mehr Blätter als vorher hat?
(c) Wie oft müssen Sie feuer anwenden, um den Baum komplett zu zerstören? Der Baum ist komplett zerstört,
wenn nur noch die Wurzel übrig ist: T []. Welche wichtige Größe spielt hier mit?
(d) Schreiben Sie eine Prozedur, die mithilfe von feuer diese Größe bestimmt!
Lösung 7.8:
(a) fun feuer ( T nil ) = T nil
| feuer ( T ts ) =
T ( foldl ( fn (x , a ) ⇒ if x = T [] then a else a @ [ feuer x ]) nil ts )
(b) Der Baum hat nach dem Brand maximal so viele Blätter, wie er vorher hatte (durch jedes zerstörte Blatt
wird maximal ein Knoten zu einem neuen Blatt).
(c) feuer muss t-mal angewendet werden. Die wichtige Größe ist die Tiefe t. Jeder Aufruf von feuer verringert
die Tiefe des Baumes um 1.
(d) fun d ( T []) = 0
| d t
= d ( feuer t ) + 1
Aufgabe 7.9 (Achtung, nicht ganz nach Schema!)
Deklarieren Sie eine Prozedur even: tree → bool, die für einen Baum true ausgibt, wenn die Anzahl der
Knoten gerade ist und ansonsten false ausgibt. Verwenden Sie nur Werte vom Typ bool und Prozeduren.
(a) Deklarieren Sie even ohne fold.
(b) Deklarieren Sie even mit fold.
Tipp: Verwenden Sie den Gleichheits-Operator (=). Zwei Bäume zusammen betrachtet (ohne den Vorgänger,
der beide verbindet) haben genau dann eine gerade Anzahl an Knoten, wenn beide Bäume eine gerade
Anzahl Knoten haben oder beide eine ungerade Anzahl Knoten haben.
Lösung 7.9:
(a) fun even t = let
fun even ’ ( T ts , b ) = foldl even ’ ( not b ) ts
in
even ’ (t , true )
end
not b berücksichtigt den Knoten, auf den even gerade aufgerufen wird.
(b) val even = fold ( foldl op= false )
Der Startwert ist false, da er den Knoten, auf dem even aufgerufen wird, repräsentiert, denn der einzelne
Knoten ist ungerade, also false.
5
3
Markierte Bäume
Aufgabe 7.10
Schreiben Sie zwei Prozeduren, die die Größe und die Tiefe von markierten Bäumen bestimmen.
Lösung 7.10:
fun size ( L (_ , ts )) = foldl op+ 1 ( map size ts )
fun depth ( L (_ , ts )) = 1 + foldl Int . max ∼1 ( map depth ts )
Aufgabe 7.11
Schreiben Sie eine Prozedur greatest: int ltr → int, die die größte Markierung eines markierten Baumes
zurückgibt.
Lösung 7.11:
fun greatest ( L (n , ts )) = foldl Int . max n ( map greatest ts )
Aufgabe 7.12
Schreiben Sie eine Prozedur avg: int ltr → int, die den (abgerundeten) Durchschnitt aller Marken zurückgibt.
Lösung 7.12:
fun avg ’ ( L (n , ts )) =
foldl ( fn (( a , b ) , (c , d )) ⇒ ( a + c , b + d )) (n , 1) ( map avg ’ ts )
fun avg lt = let
val ( summe , anzahl ) = avg ’ lt
in
summe div anzahl
end
Aufgabe 7.13
Schreiben Sie eine Prozedur exists: (α → bool) → α ltr → bool, die bestimmt, ob im Baum eine Marke
existiert, die eine Bedingung p erfüllt.
Lösung 7.13:
fun exists p ( L (x , ls )) =
foldl ( fn (y , a ) ⇒ y orelse a ) ( p x ) ( map ( exists p ) ls )
Aufgabe 7.14
Schreiben Sie eine Prozedur toLtr: α → tree → α ltr, die einen reinen Baum in einen markierten Baum
überführt, indem alle seine Knoten mit einem übergebenen Wert markiert werden. Der Aufruf toLtr 2 t sollte
also einen Baum t wie folgt in einen markierten Baum überführen:
•
•
• •
2
•
2
2
2 2
Schreiben Sie die Prozedur sowohl mit als auch ohne die Hilfsprozedur fold.
6
Lösung 7.14:
fun toLtr x ( T ts ) = L (x , map ( toLtr x ) ts )
Alternative:
fun toLtr x t = fold ( fn xs ⇒ L (x , xs )) t
4
Baumuniversität
Aufgabe 7.15 (fold lesen)
Was berechnet die Prozedur val mystery = fold T?
Lösung 7.15:
Die Identität auf Bäumen.
Aufgabe 7.16
Schreiben Sie eine Prozedur size: tree → int, die endrekursiv die Größe eines Baumes bestimmt. Überlegen
Sie sich zunächst, warum die Berechnungen mit fold oder foldl und map nicht endrekursiv sind. Orientieren
Sie sich stattdessen an der Prozedur prest aus dem Buch, die zu einer Pränummer den dazugehörigen Teilbaum
liefert. Schreiben Sie zunächst eine endrekursive Hilfsprozedur size’: tree list → int → int.
Lösung 7.16:
fun size ’ nil n = n
| size ’ (( T ts ):: tr ) n = size ’ ( ts @ tr ) ( n + 1)
fun size t = size ’ [ t ] 0
Aufgabe 7.17 (Bäume bauen)
Dieter hat gelernt, dass ein Baum durch die Menge seiner möglichen Adressen beschrieben werden kann. Da
ihm diese Darstellung besser gefällt, sind alle seine Bäume vom Typ int list list. Damit Sie mit Dieters
Bäumen etwas anfangen können, müssen Sie einen Baum aus einer Menge von Adressen konstruieren. Da Dieter
ordentlich ist, können Sie davon ausgehen, dass Sie nur gültige Bäume bekommen und dass die Adressen strikt
sortiert sind. Beispiel für eine solche Liste: [ [] , [1] , [1,1] , [1,2] , [2] ]
Schreiben Sie eine Prozedur buildTree: int list list → tree, mit der Sie Dieters Baumdarstellung in die
Ihnen vertraute Darstellung konvertieren können!
Lösung 7.17:
replaceNth xs n y ersetzt das n-te Listenelement durch y. insertInTree(a, t) fügt einen neuen Knoten,
der als Adresse a übergeben wird, in den Baum t ein. Wir bauen den Baum, indem wir alle Adressen einfügen.
fun replaceNth
nil
n y = raise Subscript
| replaceNth ( x :: xr ) 0 y = y :: xr
| replaceNth ( x :: xr ) n y = x ::( replaceNth xr ( n − 1) y )
fun insertInTree ( nil ,
| insertInTree ([ a ] ,
| insertInTree ( a :: ar ,
T ( replaceNth ts
t)
= t
T ts ) = T ( ts @ [ T []])
T ts ) =
( a − 1) ( insertInTree ( ar , List . nth ( ts , a − 1))))
fun buildTree adrlist = foldl insertInTree ( T []) adrlist
Aufgabe 7.18 (Baumschüler Dieter Schlau, heute mal ganz linear)
(a) Schreiben Sie eine Prozedur postlin: tree → int list, die die Postlinearisierung eines Baumes bestimmt.
7
(b) Und nun zurück: Schreiben Sie eine Prozedur parsePostLin: int list → tree, die aus einer Postlinearisierung den Baum rekonstruiert.
Lösung 7.18:
(a) fun postlin ( T ts ) = List . concat ( map postlin ts ) @ [ length ts ]
(b) fun parsepost ’ ([
] , stack ) = stack
| parsepost ’ (( x :: xr ) , stack ) =
let
val (h , t ) = iter x ([] , stack )
( fn (h , t ) ⇒ ( hd t :: h , tl t ))
handle Empty ⇒ raise Invalid
in
parsepost ’ ( xr , T ( rev h ) :: t )
end
fun parsepost pl = case parsepost ’ ( pl ,[]) of [ r ] ⇒ r | _ ⇒ raise Invalid
8
Herunterladen