Prof. Dr. A. Poetzsch-Heffter Dipl.-Inform. J.O. Blech Dipl.-Inform. M.J. Gawkowski Dipl.-Inform. N. Rauch Technische Universität Kaiserslautern Fachbereich Informatik AG Softwaretechnik Übungsblatt 3: Softwareentwicklung I (WS 2006/07) Ausgabe: 7. November 2006 Abgabe: 13. November 2006, 12.00 Uhr Abnahme praktischer Teil: xxx. November 2003 Aufgabe 1 Operatorpräzedenzen (3 Punkte) Gegeben seien die folgenden ML-Funktionsdeklarationen: a) fun f (x, y) = x + y * y >= x; (1 Punkt) b) fun m (a, b) = b + a div b = b mod a; (1 Punkt) Machen Sie die Operatorpräzedenzen explizit, indem Sie beide Ausdrücke gemäß den Präzedenzen der auftretenden Operatoren klammern, und begründen Sie Ihre Antwort. Beispiel: Aus dem Ausdruck not x andalso y orelse x andalso not y wird der Ausdruck (((not x) andalso y) orelse (x andalso (not y))). Hinweis: Eine Auflistung der Präzedenzen einzelner Operatoren finden Sie auf der Seite von Standard ML of New Jersey, http://www.smlnj.org/doc/basis/pages/top-level-chapter.html infix infix infixr infix infix infix 7 6 5 4 3 0 * / div mod + - ^ :: @ = <> > >= < <= := o before All Operatoren, die hier aufgelistet wurden, assozieren nach links, z.B.: Der Ausdruck 3+4+5 wird als (3+4)+5 interpretiert und nicht als 3+(4+5). c) Welchen Typ haben die Funktionen f und m? Begründen Sie Ihre Antwort. Aufgabe 2 Syntax-, Typisierungs- und Laufzeitfehler (1 Punkte) (5 Punkte) Schreiben Sie die unten aufgelisteten Ausdrücke in einer sml-Datei auf und bestimmen Sie experimentell, zu welchem Ergebnis das Auswerten von ihnen führt. Zu jedem Ausdruck beantworten Sie die folgenden Fragen: Zu welchem Ergebnis führt das Auswerten des Ausdrucks? Was für ein Fehler ist eventuell aufgetreten? Was ist die Ursache des Fehlers? Für den Fall, dass man einem Ausdruck eine sinnvolle Semantik zuordnen kann, schlagen Sie eine Verbesserung vor. a) 13.6 * 0.89; b) 2 + 2 = 4.0; c) implode(rev (explode "Reit nie tot ein Tier")); d) ((~4)=(~(1+3)))=true; e) "Erstsemestler" > "Zweitsemestler"; f) true=(2<>4)=(true orelse not true); g) false <> true; h) ((6 mod 5)=5) orelse false; i) (fn x => (x + 1))[]; j) (if true then 1 else 2 = false)=1; k) (if 3.0 < 4 then true else false); l) if "true" then 13 else 14; m) "bi"^(if false then "la" else "ba")^"bu"; n) #1(false,true); o) ((10 mod 2), true) = (5-(4+1),if true then (not not true) else false); p) x=true; q) val my_? = "myquestionmark"; r) 1.0e~3; s) (1.0e~3)=0.001; t) (fn x => (real(trunc x)) + (x - (real(trunc x))) ) 3.14; Aufgabe 3 Die Datenstruktur String (praktisch) (4 Punkte) Schreiben Sie eine ML-Funktion dual2decimal vom Typ string → int, die eine Zeichenkette, die eine binäre Zahl ibin darstellt, als Eingabe nimmt und diese in den Integerwert von ibin konvertiert. Beispiele: dual2decimal "" = 0 dual2decimal "0" = 0 dual2decimal "1" = 1 dual2decimal "100" = 4 dual2decimal "111" = 7 dual2decimal "00101" = 5 Aufgabe 4 Rekursion (praktisch) (10 Punkte) a) Schreiben Sie eine ML-Funktion fakultaet : int → int, die zu einer ganzen Zahl n ≥ 0 das Produkt aller Zahlen k mit 1 ≤ k ≤ n, also n!, berechnet. (2 Punkte) Beispiele: 1. Der Wert von 0! vordefiniert 1. 2. 1! = 1 3. 2! = 1 ∗ 2 = 2 4. 3! = 1 ∗ 2 ∗ 3 = 6 b) Schreiben Sie eine ML-Funktion ngeraden : int → int list, die zu einer ganzen Zahl n ≥ 0 eine Liste von n ersten geraden Zahlen berechnet. Beispiele: 1. ngeraden(0) = [] 2. ngeraden(5) = [0, 2, 4, 6, 8] c) Schreiben Sie eine ML-Funktion power : int ∗ int → int, die zu zwei ganzen Zahlen n und k ≥ 0 die k-te Potenz von n, also nk , berechnet. (2 Punkte) d) Schreiben Sie eine ML-Funktion findapprox : real ∗ real ∗ (real list) → int ∗ int, die ein Tupel (d, n, l) als Argument entgegennimmt und ein Paar von ganzen Zahlen (status, erg) zurückgibt. Dabei soll findapprox die folgende Funktionalität besitzen: Kommt in der Liste l mindestens eine Zahl n0 mit |n−n0 | < d vor, dann soll das Ergebnis als (0, i) definiert werden, wobei i der kleinste Listen-Index mit |n − (List.nth(l, i))| < d. Kommt in der Liste keine solche Zahl, d.h. ∀ i. 0 ≤ i < List.length(l) −→ |n − (List.nth(l, i))| > d, dann soll die Funktion (−1, −1) als das Ergebnis zurückliefern. (2 Punkte) Beispiele: 1. findapprox(1.12, 47.34, []) = (−1, −1) 2. findapprox(1.12, 47.34, [−3.4, 78.1345, 46.21]) = (−1, −1) 3. findapprox(1.12, 47.34, [−3.4, 78.1345, 46.21, 47.0, 47.2]) = (0, 3) e) (Die verschränkte Rekursion) Betrachten Sie das folgende Beispiel für verschränkt rekursive Funktionsdelarationen. 1 2 3 4 5 6 fun odd n = if n=0 then false else (if n=1 then true else even(n-1)) and even n = if n=0 then true else (if n=1 then false else odd(n-1)) 7 8 9 10 11 12 13 14 15 16 17 18 19 - even val it - even val it - even 0; = true : bool 1; = false : bool 2; 20 21 22 23 24 val it = true : bool - even 3; val it = false : bool - odd 0; val it = false : bool - odd 1; val it = true : bool - odd 2; val it = false : bool - odd 3; val it = true : bool - k+1 1 Aufgabestellung: Es sei eine Potenzreihe 1− 12 + 13 − 14 +. . . = Σ∞ k=1 (−1) k gegeben. Bekanntlich, approximiert diese Reihe den Wert ln 2. Schreiben Sie eine Funktion log2 : int → real, die zu einer ganzen Zahl n ≥ 1 die Summe Σnk=1 (−1)k+1 k1 berechnet. Ihre Funktionsdeklaration soll auf dem oben veranschaulichten Prinzip der verschränkten Rekursion basieren. Vergleichen Sie die Genauigkeit des Ergebnises des Aufrufes log2(n) mit dem Ergebnis des Aufrufs von Math.ln 2.0;. (2 Punkte) Aufgabe 5 Binärer Zähler (praktisch) (6 Punkte) Definieren Sie eine ML-Funktion tick, die einen tickenden binären Zähler simuliert. tick soll eine Liste von booleschen Werten b, die eine binäre Zahl darstellt (hierbei soll true den Wert 1 repräsentieren und false den Wert 0), als Eingabe nehmen und eine Liste von booleschen Werten b’, die eine binäre Zahl darstellt, zurückgeben. Dabei soll der Wert der binären Zahl, die von b’ dargestellt wird, um 1 größer sein als der Wert der Zahl, die von b dargestellt wird. Beispiele: tick([]) = [true] tick([false]) = [true] tick([true,false]) = [true,true] tick(tick(tick([true,false]))) = [true,false,true] tick(tick(tick(tick(tick(tick([true,false])))))) = [true,false,false,false]. Hinweis: Bekanntlich verläuft schriftliches Addieren zweier ganzen Zahlen von rechts nach links. Dagegen werden Listen von links nach rechts verarbeitet. Nutzen Sie ggf. eine Funktion der Datenstruktur List. Aufgabe 6 Terminierung Betrachten Sie die folgenden Funktiondeklarationen der Funktionen find1 und find2. 51 52 fun find_helper1(n,e,l) = if e=List.nth(l,n) (2 Punkte) 53 54 then n else find_helper1(n+1,e,l) 55 56 fun find1 (e,l) = find_helper1(0,e,l) 57 58 59 60 61 62 fun find_helper2(n,e,l) = if Real.<(Real./(e,List.nth(l,n)),1.0) then n else find_helper2(n+1,e,l) 63 64 fun find2 (e,l:real list) = find_helper2(0,e,l) 65 Beschreiben Sie, für welche Eingaben diese Funktionen terminieren und für welche nicht. Schlagen Sie sinnvolle Verbesserungen für diese Deklarationen vor, so dass diese immer terminieren.