Fachrichtung 6.2 — Informatik Universität des Saarlandes Tutorenteam der Vorlesung Programmierung 1 Programmierung 1 (Wintersemester 2015/16) Aufgaben für die Übungsgruppen: 13 (Kapitel 14 & 15) 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. Datenstrukturen Aufgabe 13.1 (Snoc) Schreiben Sie eine Prozedur snoc: α → α vector → α vector, die zu x und [x1, x2, x3, ..., xn] den Vektor [x1, x2, x3, ..., xn, x] liefert. Aufgabe 13.2 structure S = struct val a = 4 fun f x = x + 1 end Sei obige Struktur in der leeren Umgebung deklariert. Ist der Aufruf val test = f a in SML gültig? Aufgabe 13.3 (a) Gegeben sei folgende Struktur: (b) Gegeben sei folgende Struktur: structure Ident :> sig val id : string → string val x : real end = struct fun id x = x val x = id 14.4 end structure Test :> sig eqtype α ls val new : α list → α ls end = struct type α ls = α list fun new xs = xs end Warum ist folgender Vergleich unzulässig, obwohl α ls als Typ mit Gleichheit implementiert wurde? Warum ist der Aufruf von Ident.id mit dem Argument 13.4 nicht zulässig, obwohl die Prozedur polymorph implementiert wurde und in der Struktur sogar mit dem Argument 14.4 aufgerufen wird? val a = [1 ,3 ,5] val b = Test . new a val istGleich = a = b Aufgabe 13.4 Schreiben Sie eine Struktur MyMath, die folgende Operationen enthält: • • • • • fac: int → int (berechnet die Fakultät einer natürlichen Zahl, wobei für n < 0 gilt: fac n = 0) add: int * int → int (addiert zwei Ganzzahlen) addr: real → real → real (addiert zwei Reals) mul: int * real → real (multipliziert zwei Zahlen) sum: int list → int (berechnet die Summe der Elemente einer Liste, wobei sum nil = 0) Dabei sollen fac, mul und sum lineare Laufzeit und add und addr konstante Laufzeit haben. 1 Aufgabe 13.5 (Mengen I ) Seien Mengen von Ganzzahlen als strikt sortierte Listen dargestellt und folgende Signatur gegeben: signature MYSET = sig type set val subset : set → set → bool val union : set → set → set end (a) Schreiben Sie eine Struktur MySet, die folgende Operationen in linearer Laufzeit ausführen kann: • subset: set → set → bool (testet, ob eine Menge Teilmenge einer anderen Menge ist) • union: set → set → set (liefert die Vereinigung zweier Mengen) (b) Schreiben Sie eine Prozedur equal: MySet.set → MySet.set → bool, die testet, ob zwei Mengen gleich sind. Aufgabe 13.6 (Mengen II ) Implementieren Sie eine Datenstruktur MySet2, die Mengen ganzer Zahlen darstellt. Die Datenstruktur soll dabei folgende Signatur haben: signature MYSET2 = sig type set val new : int list → set val add : int * set → set val min : set → int val max : set → int val del : int * set → set end Für die einzelnen Prozeduren sollen folgende Einschränkungen gelten: • new soll aus einer Liste eine Menge erstellen und eine Komplexität in O(n2 ) haben. • add soll ein weiteres Element in eine Menge einfügen. Die Komplexität von add soll O(n) sein. • min und max sollen den kleinsten/größten Wert einer Menge zurückgeben und konstante Komplexität besitzen. Ist die Menge leer, so soll die Ausnahme Empty geworfen werden. • delete soll ein Element aus der Menge entfernen und lineare Komplexität besitzen. Aufgabe 13.7 (Binäre Suche) Gegeben sei ein sortierter Vektor, in dem nun nach einer bestimmten Komponente gesucht wird und deren Position zurückgegeben werden soll. Anstatt nun von vorn nach hinten den ganzen Vektor zu durchlaufen und jede Komponente zu überprüfen (lineare Suche), betrachten wir zuerst die Komponente in der Mitte des Vektors. Falls es sich um die gesuchte Komponente handelt, ist die Suche schon beendet. Andernfalls wird die Suche fortgesetzt auf entweder dem linken Teil oder dem rechten Teil des Vektors, je nachdem ob die mittlere Komponente zu klein oder zu groß war. Die Suchprozedur position: (α * α → order) → α vector → α → int option liefert einen Optionstypen zurück, da die Suche natürlich auch erfolglos sein kann. Die Hilfsprozedur position’ bekommt immer den Bereich des Vektors übergeben, der durchsucht werden soll (left und right). Überlegen Sie sich, welche Komplexität position hat, und komplettieren Sie die Prozedur: fun position compare v x = let fun position ’ left right = if left > right then ... else ... in position ’ 0 ( Vector . length v − 1) end 2 Aufgabe 13.8 (Mysteriöse Listen) Implementieren Sie eine Datenstruktur, die die folgende Signatur und die gegebenen Einschränkungen erfüllt: signature MYSTERYLIST = sig type α mlist val empty : α mlist (* stellt eine leere Liste dar *) val mlist : α list → α mlist val cons : α → α mlist → α mlist (* Fuegt ein Element in konstanter Zeit hinzu *) val rev : α mlist → α mlist (* Reversiert in konstanter Zeit *) val hd : α mlist → α (* gibt das erste Element zurueck , Laufzeit egal *) val tl : α mlist → α mlist (* gibt den tail der Liste in konstanter Zeit zurueck *) end Speicher Aufgabe 13.9 (Statische Semantik für Speicheroperationen) Ergänzen Sie folgende Inferenzregeln der statischen Semantik für Allokation, Dereferenzierung und Zuweisung: Sref T ` : S! T ` S:= : T ` : Aufgabe 13.10 (Quiz I ) Wozu werten die folgenden Ausdrücke aus? (a) ref 1 = ref (b) ref 1 = ref (c) val a = ref val b = a a = b (d) ! ( ref 0) = (e) ! ref 1 = ! (f) map ! [ ref 1 , ref 2 , ref 3] = [ ref 1 , ref 2 , ref 3] 2 1 1 (g) map ! [ ref 1 , ref 2 , ref 3] = [1 , 2 , 3] (h) map ! [] (i) map ! [] = [] ref 2 ref 1 (j) ref [1 , 2 , 3] = [ ref 1 , ref 2 , ref 3] Aufgabe 13.11 (Quiz II ) Sind folgende Phrasen zulässig? Wenn ja, wozu werten sie aus? (d) val rl = [ ref 1 , ref 2 , ref 3] (a) map ! (! ( ref [2 ,5 ,21])) foldr := 3 rl (b) val ri = ref 8 rl val rb = ref true (e) val intref = ref 5 if true then ri else rb val intref = ref ( intref := 3; (c) val rl = [ ref 1 , ref 2 , ref 3] ! intref + 2; ! intref = 5) foldr ( fn (x , s ) ⇒ ( x := s ; s )) 3 rl rl Aufgabe 13.12 (Quiz III ) Wie unterscheiden sich die folgenden Prozeduren? • • • • val val fun fun c0 c1 c2 c3 = let val r = 0 in fn () ⇒ ( r = r = let val r = ref 0 in fn () ⇒ ( r () = let val r = ref 0 in ( r := ! r () = let val r = ref 0 in fn () ⇒ + 1; r ) end := ! r + 1; ! r ) end + 1; ! r ) end ( r := ! r + 1; ! r ) end Aufgabe 13.13 (Quiz IV ) Wozu wertet dieser Ausdruck aus? let fun mystery a b c d = b := !(#2 ( d := !a , d , d := ! c )) val (a ,b ,c , d ) = ( ref 1 , ref 2 , ref 3 , ref 4) in ( mystery a b c d ; (! a , !b , !c , ! d )) end 3 Aufgabe 13.14 (Generatoren) (a) Schreiben Sie eine Prozedur zero: unit → int ref, die bei jedem Aufruf eine Referenz auf eine Speicherzelle zurückgibt, in der eine 0 steht. Die Speicherzelle muss nicht immer die gleiche sein. (b) Schreiben Sie eine Prozedur n: int → int ref, die bei jedem Aufruf eine Referenz auf eine Speicherzelle zurückgibt, in der eine Eingabe x steht. (c) Deklarieren Sie einen Bezeichner c: unit → unit, der bei jedem Aufruf einen Zähler in einer Speicherzelle um eins erhöht, sodass nach n Aufrufen die Anzahl der Aufrufe in der Speicherzelle stehen. (d) Passen Sie c: unit → int ref so an, dass bei jedem Aufruf eine Referenz auf die Speicherzelle, die den Zähler enthält, zurückgegeben wird. (e) Schreiben Sie eine Prozedur p: int → unit → int, die eine Eingabe n in eine Speicherzelle schreibt und eine Abstraktion zurückgibt, die bei jedem Aufruf den Inhalt der Speicherzelle verdoppelt und den neuen Inhalt der Speicherzelle ausgibt. (f) Schreiben Sie eine Prozedur q: unit → unit → int, die eine Prozedur zurückgibt, die nach n Aufrufen die n-te Quadratzahl zurückgibt. (g) Schreiben Sie eine Prozedur retimp: (α → α) → α → unit → α, die eine Prozedur zurückgibt, die nach n Aufrufen das Ergebnis der n-fachen Anwendung einer Prozedur f auf einen Startwert s zurückgibt. (h) Schreiben Sie eine Prozedur tausch: α ref → α ref → unit, die den Inhalt zweier Speicherzellen miteinander vertauscht. Nutzen Sie keine lokalen Deklarationen. (i) Schreiben Sie eine Prozedur fib: unit → unit → int, die eine Prozedur zurückgibt, die beim n-ten Aufruf die n-te Fibonacci-Zahl zurückgibt. Aufgabe 13.15 (a) Schreiben Sie eine Prozedur reffer: α list → α ref list, die zu einer Eingabeliste xs eine Liste zurückgibt, die Referenzen enthält, deren Werte die Werte von xs sind. (b) Schreiben Sie eine Prozedur dou: int ref list → unit, die die Werte der Referenzen einer Liste xs verdoppelt. Nutzen Sie eine Hilfsprozedur. (c) Schreiben Sie eine Prozedur sum: int list → int, die die Zahlen einer Liste xs aufaddiert. Die Summe soll dabei in einer einzigen Speicherzelle akkumuliert werden. Nutzen Sie keine funktionalen Hilfsprozeduren. (d) Schreiben Sie eine Prozedur effer: (α → β) → α list → β ref list ref, die eine Referenz auf eine Liste zurückgibt, die Referenzen auf die Werte der Eingabeliste xs nach Anwendung einer Prozedur f auf die einzelnen Werte enthält. Nutzen Sie beliebige Prozeduren. Für [1,2,3] und fn x ⇒ 2 * x soll eine Referenz auf [ref 2, ref 4, ref 6] zurückgegeben werden. Aufgabe 13.16 (Imperative Stapel) Wir wollen Stapel als Datenstruktur implementieren. Stapel sind veränderliche Objekte. Sei die Signatur eqtype α stack val stack val push val pop val top val empty : : : : : unit → α stack α stack → α → unit α stack → unit α stack → α α stack → bool. Alle Operationen sollen konstante Laufzeit haben. stack erzeugt einen leeren Stapel, push fügt ein Element vorn hinzu, pop löscht das erste Element, top liefert das erste Element, empty prüft, ob ein Stapel leer ist, und size gibt die Anzahl der Elemente im Stapel zurück. (a) Welche Konsequenz hat es für unsere Implementierung, dass Stapel veränderliche Objekte sind? (b) Schreiben Sie die Signatur in SML und implementieren Sie Stapel. 4