Fachrichtung 6.2 — Informatik Universität des Saarlandes Tutorenteam der Vorlesung Programmierung 1 Programmierung 1 (Wintersemester 2015/16) Wiederholungstutorium Lösungsblatt 2 (Höherstufige Prozeduren, Typinferenz) Hinweis: Dieses Übungsblatt enthält von den Tutoren für das Wiederholungstutorium erstellte Aufgaben. Die Aufgaben und die damit abgedeckten Themenbereiche sind für die Klausur weder relevant noch irrelevant. Typen, Bindungen und Bezeichner Aufgabe 2.1 (Typschema 1 ) Bestimmen Sie die Typschemata folgender Prozeduren: (a) fun p x y = if x then y else 2.0 (b) fun q x = #1((), x=3) (c) fun r f a b = f(a,b) (d) fun s n f x = if n < 100 then x else f(x) (e) fun t f x y = if x < 1 then f(x,y) else f(y,x) Lösung 2.1: (a) p: bool → real → real (b) q: int → unit (c) r: ((α * β) → γ) → α → β → γ (d) s: int → (α → α) → α → α (e) t: ((int * int) → α) → int → int → α Aufgabe 2.2 (Typinferenz) Schreiben Sie Prozeduren mit folgenden Typschemata, ohne dabei explizite Typangaben zu verwenden: (a) (bool * int) → int (b) int → bool → int → unit (c) ∀αβ. int → α → (α → β) → α (d) ∀αβ. ’α → (’α * β) → β → β (e) ∀αβγ. (α * (β → α → γ)) → (α → β) → γ Lösung 2.2: (a) fun a (x , y ) = if x then y else 3 (b) fun b x y z = if y then #2( x+z ,()) else () (c) fun c x y f = if x < 1 then y else #1( y , f ( y )) (d) fun d x ( y1 , y2 ) z = if x = y1 then y2 else z (e) fun e (x , f ) g = ( f ( g x ) x ) 1 Aufgabe 2.3 (Typschema 2 ) Geben Sie die Typschemata folgender Bezeichner an: (a) val a = fn x ⇒ ( fn y ⇒ if y then x else 3) (b) val b = fn x ⇒ ( fn y ⇒ ( fn f ⇒ f (x , y ) true )) (c) val c = let fun f x y = if x then y else 2 in ( fn t ⇒ fn s ⇒ ( f s t ,())) end Lösung 2.3: (a) a: int → bool → int (b) a: α → β → ((α * β) → bool → γ) → γ (c) b: int → bool → (int * unit) Aufgabe 2.4 (Bezeichnerbindungen) Bereinigen Sie folgende Prozedurdeklaration: Überstreichen Sie die definierenden Bezeichnerauftreten, stellen Sie die lexikalischen Bindungen mit Pfeilen dar und benennen Sie konsistent die gebundenen Bezeichner um. fun g x y = ( fn x ⇒ let val x = f x val y = g x y in x + y end ) x Lösung 2.4: fun g1 x1 y1 = ( fn x2 ⇒ let val x3 = f x2 val y2 = g1 x2 y1 in x3 + y2 end ) x1 Lexikalische Bindungen: Ziehe einen Pfeil von jedem indizierten, benutzenden Bezeichnerauftreten zu dem gleich indizierten, überstrichenen Bezeichnerauftreten. Aufgabe 2.5 (Bindungsprobleme) Bereinigen Sie das folgende Programm durch Überstreichen der definierenden und Indizieren der gebundenen Bezeichnerauftreten. val fun val fun (x , y ) = (2 ,3) f x = fn x ⇒ #1( x , y ) y = x*y g g = f x g Machen Sie sich zunächst klar, wie die Deklaration der Prozedur g zu verstehen ist. Lösung 2.5: val fun val fun (x1 , f1 x2 y2 = g1 g2 y1 ) = (2 ,3) = fn x3 ⇒ #1( x 3 ,y 1 ) x1 * y1 = f1 x1 g2 Kaskadierte und Höherstufige Prozeduren Aufgabe 2.6 (Konstant) Schreiben Sie eine Prozedur constant: α → β → α nach Möglichkeit auf drei verschiedene Weisen: kaskadiert, mit einer lokalen Deklaration und mit einer Abstraktion. Die Prozedur soll immer konstant das erste Argument zurückgeben, egal was als zweites Argument eingegeben wird. 2 Lösung 2.6: • fun constant x y = x • fun constant x = let fun const y = x in const end • fun constant x = ( fn y ⇒ x ) Aufgabe 2.7 (Kaskadiert und höherstufig) Schreiben Sie folgende Prozeduren: (a) eine höherstufige Prozedur, die nicht kaskadiert ist. (b) eine kaskadierte Prozedur, die nicht höherstufig ist. (c) eine höherstufige, kaskadierte Prozedur. Lösung 2.7: (a) fun f ( x : int → int ) : int = 4 (b) fun f x y = x + y (c) fun f g x = g x Aufgabe 2.8 (Konvertieren) Schreiben Sie zwei Prozeduren (a) cas: (int * int → int) → int → int → int (b) car: (int → int → int) → int * int → int so, dass cas zur kartesischen Darstellung einer zweistelligen Operation die kaskadierte Darstellung und car zur kaskadierten Darstellung die kartesische Darstellung liefert. Lösung 2.8: (a) fun cas ( f : int * int → int ) ( x : int ) ( y : int ) = f (x , y ) (b) fun car ( f : int → int → int ) ( x : int , y : int ) = f x y Aufgabe 2.9 (Höherstufiges Produkt) Deklarieren Sie eine Prozedur prod: (int → int) → int → int, die für n > 0 die Gleichung prod f n = 1·(f 1)·. . .·(f n) erfüllt. Deklarieren Sie außerdem mithilfe von prod eine Prozedur fac: int → int, die für n > 0 die Fakultät n! berechnet. Lösung 2.9: fun prod f n = if n<1 then 1 else ( f n ) * prod f ( n−1) fun fac z = prod ( fn y ⇒ y ) z 3 Aufgabe 2.10 (Primzahlen 1 ) Deklarieren Sie zunächst eine Prozedur forall: int → int → (int → bool) → bool, sodass forall m n p genau dann true liefert, wenn p für alle natürlichen Zahlen von m bis n (inklusive) das Ergebnis true liefert. Verwenden Sie andalso und orelse. Schreiben Sie anschließend mithilfe von forall eine Prozedur isprime: int → bool, die testet, ob eine Zahl x eine Primzahl ist. Lösung 2.10: fun forall m n p = m > n orelse ( p m andalso forall ( m+1) n p ) fun isprime x = x >= 2 andalso forall 2 ( sqrt x ) ( fn y ⇒ x mod y <> 0) Aufgabe 2.11 (Wahrheitszähler) Schreiben Sie eine Prozedur tcount: int → int → (int → bool) → int, welche zu zwei Zahlen n, m > 0 die Anzahl der Zahlen v mit n ≤ v ≤ m berechnet, für die eine Prozedur p den Wert true ergibt. Verwenden Sie eine endrekursive Hilfsprozedur tcount’. Die Prozedur tcount selbst soll nicht rekursiv sein. Deklarieren Sie außerdem mithilfe von tcount eine Prozedur pcount: int → int, die zählt, wieviele Zahlen kleinergleich n Primzahlen sind. Lösung 2.11: fun tcount ’ a n m f = if n > m then a else if f n then tcount ’ ( a+1) ( n+1) m f else tcount ’ a ( n+1) m f fun tcount n m f = tcount ’ 0 n m f fun pcount z = tcount 0 z isprime Iter und First Aufgabe 2.12 (Primzahlen 2 ) Schreiben Sie unter Verwendung der Prozedur isprime aus Aufgabe 2.10 folgende Prozeduren: (a) nextprime: int → int, sodass nextprime zu einer Zahl n die erste Primzahl x > n liefert. (b) nthprime: int → int, die für n ≥ 1 die n-te Primzahl liefert. Lösung 2.12: (a) fun nextprime n = first ( n+1) isprime (b) fun nthprime n = iter n 1 nextprime Aufgabe 2.13 (Iter Rocks) Deklarieren Sie folgende Prozeduren mithilfe von iter: (a) add: int → int → int, die zwei Zahlen x, y durch Inkrementieren addiert. (b) power: int → int → int, zur Berechnung von Potenzen. (c) sum: int → (int → α) → α, sodass sie für n ≥ 0 die Gleichung sum n f = 0 + f 1 + ... + f n erfüllt. Benutzen Sie Tupel und eine Projektion. Lösung 2.13: (a) fun add x y = iter y x ( fn s ⇒ s+1) (b) fun power x n = iter n 1 ( fn a ⇒ a * x ) (c) fun sum n f = #1( iter n (0 ,1) ( fn (x , y ) ⇒ ( x + ( f y ) , y+1))) 4 Aufgabe 2.14 (Fakultät) Deklarieren Sie n! mit iterup. Lösung 2.14: fun fac n = iterup 1 n 1 ( fn (x , s ) ⇒ x * s ) Aufgabe 2.15 (Gerade oder ungerade) Schreiben Sie eine Prozedur gerade: int → bool, die für eine Zahl n prüft, ob sie gerade oder ungerade ist. Verwenden Sie dazu ausschließlich iter und weder die Operation mod noch div. Lösung 2.15: fun gerade n = iter n true not Alternative: fun gerade n = iter n true ( fn x ⇒ if x then false else true ) Aufgabe 2.16 (Summe) Schreiben Sie eine Prozedur gsum: int → int → int, die zu zwei natürlichen Zahlen m, n mit m < n die Summe m + (m + 1) + . . . + (n − 1) + n berechnet. Verwenden Sie dabei folgende Prozeduren: (a) iterup (b) iter Lösung 2.16: (a) fun summe (m , n ) = iterup m n 0 ( fn (m , s ) ⇒ m + s ) (b) fun summe (m , n ) = #1( iter ( n−m ) (0 , m+1) ( fn (a , b ) ⇒ ( a + b , b+1))) Aufgabe 2.17 (Think Diverent) Schreiben Sie die Prozedur div zur ganzzahligen Division auf folgende alternative Weisen: (a) fdiv: int → int → int, mithilfe von first. (b) idiv: int → int → int, mithilfe von iter. Hinweis: Verwenden Sie Tupel und eine Projektion für Aufgabenteil b). Lösung 2.17: (a) fun fdiv x y = ( first 0 ( fn a ⇒ a * y > x )) − 1 (b) fun idiv x y = #1( iter x (0 ,1) ( fn (z , v ) ⇒ if v = y then ( z+1 ,1) else (z , v+1))) 5 Aufgabe 2.18 (First erweitern) Deklarieren Sie folgende Prozeduren: (a) eine Prozedur second: (α → true) → int, die die zweite Zahl x > 0 findet, für die eine Prozedur p den Wert true ergibt. Verwenden Sie first. (b) eine Prozedur, die allgemein die k-te Zahl findet, für die eine Prozedur p true ergibt. Lösung 2.18: (a) fun first s p = if p s then s else first ( s+1) p fun second p = let val ersteZahl = first 0 p in first ( ersteZahl + 1) p end (b) fun find k p = iter k 0 ( fn x ⇒ first ( x+1) p ) Aufgabe 2.19 (Iter erweitern) Deklarieren Sie mithilfe der Prozedur iter eine Prozedur: (a) iterup’, die zu m, n, s, und f dasselbe Ergebnis liefert wie iterup m n s f. (b) iterdn’, die zu m, n, s, und f dasselbe Ergebnis liefert wie iterdn m n s f. Lösung 2.19: (a) fun iterup ’ m n s f = #2( iter ( n−m+1) (m , s ) ( fn (x , a ) ⇒ (( x+1) , f (x , a )))) (b) fun iterdn ’ m n s f = #2( iter ( n−m+1) (n , s ) ( fn (x , a ) ⇒ (( x−1) , f (x , a )))) 6