Fachrichtung 6.2 — Informatik Universität des Saarlandes Tutorenteam der Vorlesung Programmierung 1 Programmierung 1 (Wintersemester 2015/16) Lösungsblatt: Aufgaben für die Zusatztutorien: 8 (Speicher) Speicher und andere Späße Aufgabe 8.1 Schreiben Sie eine Prozedur swap: α ref → α ref → unit, welche die Werte zweier Referenzen vertauscht. Lösung 8.1: fun swap r r ’ = r := #1(! r ’ , r ’ := ! r ) Aufgabe 8.2 Sind folgende Phrasen zulässig und wenn ja, wozu werten sie aus? (a) map ! (! ( ref [2 ,5 ,21])) (b) val ri = ref 8 val rb = ref true if true then ri else rb (c) val rl = [ ref 1 , ref 2 , ref 3] foldr ( fn (x , s ) ⇒ ( x := s ; s )) 3 rl rl (d) val rl = [ ref 1 , ref 2 , ref 3] foldr := 3 rl rl (e) val intref = ref 5 val intref = ref ( intref := 3; ! intref + 2; ! intref = 5) Lösung 8.2: (a) Type clash: type ’int list’ does not match function’s argument type (b) Type clash: int ref <> bool ref (c) [ref 3, ref 3, ref 3] (d) Type clash: int <> unit (e) ref false Aufgabe 8.3 Wie unterscheiden sich die folgenden Prozeduren? • val c0 = let val r = 0 in fn () ⇒ ( r = r + 1; r ) end • val c1 = let val r = ref 0 in fn () ⇒ ( r := ! r + 1; ! r ) end 1 • fun c2 () = let val r = ref 0 in ( r := ! r + 1; ! r ) end • fun c3 () = let val r = ref 0 in fn () ⇒ ( r := ! r + 1; ! r ) end Lösung 8.3: • c0 ist eine rein funktionale Prozedur die Konstant 0 liefert • c1 ist ein Generator für die positiven natürlichen Zahlen • c2 ist eine imperative Prozedur die Konstant 1 liefert • c3 ist ein Generator für Generatoren wie c1 Aufgabe 8.4 (a) Binden Sie eine Speicherzelle mit dem Inhalt 1337 an den Bezeichner leet. (b) Schreiben Sie eine Prozedur uberleet : unit → unit, welche bei jedem Aufruf den Wert in der Speicherzelle, welche an den Bezeichner leet gebunden ist um eins hochzählt. Lösung 8.4: (a) val leet = ref 1337 (b) fun uberleet () = leet := ! leet +1 Aufgabe 8.5 Schreiben Sie einen Counter counterpot : unit → int, welcher Ihnen bei jedem Aufruf die nächste Zweierpotenz liefert. Sie düfen 20 als Startwert nutzen, der erste Aufruf von counterpot soll 2 ausgeben. Dabei soll die Deklaration die Zelle einkapseln. Lösung 8.5: val counter = let val r = ref 1 in fn () ⇒ ( r := ! r *2 ;! r ) end W und M Aufgabe 8.6 Wenn Sie es noch nicht getan haben: Bearbeiten Sie Aufgabe 16.3, 16.4 und 16.5 im Buch. Aufgabe 8.7 Schreiben Sie ein Maschinenprogramm, das den konditionalen Ausdruck if 2*3 ≤ 12 - 3 then 3 else 5 auswertet. Lösung 8.7: [ con 3 , con 12 , sub , con 3 , con 2 , mul , leq , cbranch 3 , con 3 , branch 2 , con 5 , halt ] 2 Aufgabe 8.8 Schreiben Sie eine endrekursive Prozedur, ein Programm in W und ein Programm in M , welches Multiplikation mit wiederholter Addition berechnet. Lösung 8.8: Endrekursiv: fun mul ’ x y a = if y<1 then a else mul ’ x ( y−1) ( a+x ) ( fun mul x y = mul ’ x y 0) In W: (* var x *) (* var y *) var a := 0 while 1<=y do a := a+x ; y := y−1 end return a In M: Die Argumente x und y liegen schon an den Stellen 0 und 1 auf dem Stapel. [ con 0 , getS 1 , con 1 , leq , cbranch 10 , getS 2 , getS 0 , add , putS 2 , con 1 , getS 1 , sub , putS 1 , branch ∼12 , putS 1 , putS0 , halt ] Aufgabe 8.9 Schreiben Sie ein Programm in M , welches das Maximum von zwei natürlichen Zahlen n und m ausgibt. Können Sie auch das Maximum berechnen, wenn sie mit leq nur auf ≤ 0 testen dürfen? Lösung 8.9: n liegt an Position 0, m liegt an Position 1. [ getS 0 , getS 1 , leq , cbranch 2 , getS 0 , branch 2 , getS 1 , halt ] Aufgabe 8.10 • Welche Komplexität hat Positionszugriff auf Listen, Vectoren und Arrays? • Welche Komplexität hat das Hinzufügen von Elementen bei Listen, Vectoren und Arrays? Imperative Datenstrukturen Aufgabe 8.11 (Imperative Stapel) Implementieren Sie Stapel mit der folgenden Signatur: signature STACK = sig eqtype α stack val stack : unit → α stack (* liefert einen leeren Stapel *) val push : α stack → α → unit (* legt ein Element auf den Stapel *) val pop : α stack → unit (* loescht das erste Element vom Stapel *) (* Empty *) val top : α stack → α (* liefert das erste Element des Stapels *) (* Empty *) val empty : α stack → bool (* prueft , ob ein Stapel leer ist *) end Achten Sie darauf, dass alle Operation konstante Laufzeit haben. 3 Lösung 8.11: structure Stack :> STACK = struct type α stack = α list ref fun stack () = ref nil fun push s x = s := x :: ! s fun pop s = s := tl (! s ) fun top s = hd (! s ) fun empty s = null (! s ) end Aufgabe 8.12 (Imperative Schnitzeljagd, aka verkettete Listen) In dieser Aufgabe wollen wir eine Schnitzeljagd implementieren. Eine Schnitzeljagd besteht aus verschiedenen Stationen, die entweder das Ziel oder eine Zwischenstation sind. An einer Zwischenstation findet man ein Textfragment und die Position der nächsten Station (bzw. des Ziels). Man spielt das Spiel indem man von einer Startstation aus immer zur nächsten Station wandert und dabei alle Textfragmente zusammenfügt. Gewinner ist schließlich derjenige, der der das Ziel erreicht und alle Textfragmente in der korrekten Reihenfolge eingesammelt hat. datatype Station = Ziel | ZwischenS of string * Station ref Die Schnitzeljagd ist ein sehr imperatives Spiel. Man muss lediglich die Position der ersten Station kennen um alle weiteren Stück für Stück zu finden. (a) Schreiben Sie eine Prozedur createSchnitzeljagd : string list → Station ref die zu einer Liste von Textstücken die passende Schnitzeljagd erstellt und eine Referenz auf die Startstation zurückgibt. So soll beispielsweise der Aufruf mit ["Speicher␣", "ist␣", "toll!"] folgendes Konstrukt erstellen: “Speicher “ “ist “ “toll!” Ziel (b) Schreiben Sie nun eine Prozedur playSchnitzeljagd : Station ref → string die, angefangen bei der Startstation, eine Schnitzeljagd durchgeht und am Ende den gesammelten Text ausgibt. Lösung 8.12: (a) fun c r ea t e Sc h n it z e lj a g d nil = ref Ziel | c r e at e S ch n i tz e l ja g d ( x :: xs ) = let val nexts = c re a t e Sc h n it z e lj a g d xs in ref ( ZwischenS (x , nexts )) end (b) fun play Schni tzelja gd r = case ! r of Ziel ⇒ " " | ( ZwischenS ( text , nexts )) ⇒ text ^ pl aySchn itzel jagd nexts 4