Lösungen zum Übungsblatt 3: Softwareentwicklung I (WS 2006/07)

Ähnliche Dokumente
Übungsblatt 6: Softwareentwicklung I (WS 2006/07)

Verarbeitung unendlicher Datenstrukturen Jetzt können wir z.b. die unendliche Liste aller geraden Zahlen oder aller Quadratzahlen berechnen:

Klausur Programmierung WS 2002/03

Beschreibung von Werten: Beschreibung von Werten: (2) Begriffsklärung: (Ausdruck, expression) (2) Begriffsklärung: (Ausdruck, expression)

Programmierung 1 (Wintersemester 2012/13) Lösungsblatt 1 (Kapitel 1)

Funktionale Programmierung ALP I. Funktionen höherer Ordnung SS Prof. Dr. Margarita Esponda. Prof. Dr. Margarita Esponda

Programmierung und Modellierung

Die Definition eines Typen kann rekursiv sein, d.h. Typ-Konstruktoren dürfen Elemente des zu definierenden Typ erhalten.

Lösungsvorschläge zum Übungsblatt 11: Übersetzung von Programmiersprachen (WS 05/06)

Probeklausur Software-Entwicklung I

Lösungshinweise/-vorschläge zum Übungsblatt 2: Grundlagen der Programmierung (WS 2018/19)

Informatik I Übung, Woche 40

Vorsemesterkurs Informatik

Inhalt Kapitel 2: Rekursion

TECHNISCHE UNIVERSITÄT MÜNCHEN FAKULTÄT FÜR INFORMATIK

Software Entwicklung 1. Fallstudie: Arithmetische Ausdrücke. Rekursive Klassen. Überblick. Annette Bieniusa / Arnd Poetzsch-Heffter

Grundlegende Datentypen

Funktionale Programmierung Grundlegende Datentypen

Berechnungsschemata: Funktion als Parameter abstrahiert Operation im Schema, wird bei Aufruf des Schemas konkretisiert

Lösungshinweise/-vorschläge zum Übungsblatt 4: Grundlagen der Programmierung (WS 2018/19)

Software Entwicklung 1

Programmierung 1 (Wintersemester 2012/13) Lösungsblatt 4 (Kapitel 4)

3 Einführung in ML. 3.1 Überblick. 3.2 Elementare Datentypen und Funktionen Bezeichner Elementare Datentypen

Aufgabe 1 Basiswissen zur Vorlesung (8 Punkte)

ALP I. Funktionale Programmierung

Abstrakte Syntax von Prolog (1)

Lösungshinweise/-vorschläge zum Übungsblatt 3: Software-Entwicklung 1 (WS 2017/18)

Vorsemesterkurs Informatik Sommersemester Aufgabenblatt Nr. 5A. Lösung zu Aufgabe 1 (Fehler in Haskell-Quelltext: Parsefehler)

Funktionales Programmieren

Syntax der Sprache PASCAL

Grundlegende Datentypen

2.5 Listen. Kurzschreibweise: [42; 0; 16] Listen werden mithilfe von [] und :: konstruiert.

Grunddatentypen, Ausdrücke und Variablen Typkonversion, Überprüfen und Auswerten von Ausdrücken

TECHNISCHE UNIVERSITÄT MÜNCHEN FAKULTÄT FÜR INFORMATIK

Informatik für Schüler, Foliensatz 18 Rekursion

Beispiele: Funktionsabstraktion (3) Funktionsdeklaration. Funktionsdeklaration (2) Funktionsdeklaration (3) 3. Abstraktion über Funktionsbezeichner:

17. Rekursion 2. Bau eines Taschenrechners, Ströme, Formale Grammatiken, Extended Backus Naur Form (EBNF), Parsen von Ausdrücken

Methoden. Gerd Bohlender. Einstieg in die Informatik mit Java, Vorlesung vom

Programmierung 1 (Wintersemester 2015/16) Wiederholungstutorium Lösungsblatt 11 (Parser II)

Einführung in die Informatik I

Inhalt Kapitel 5: Funktionen höherer Ordnung

Praktische Informatik 3

Lösungvorschlag zum Übungsblatt 6: Software-Entwicklung I (WS 2007/08)

Informatik I: Einführung in die Programmierung 3. Werte, Typen, Variablen und Ausdrücke

Kapitel 1: Informationsverarbeitung durch Programme

Grunddatentypen, Ausdrücke und Variablen Typkonversion, Überprüfen und Auswerten von Ausdrücken

Haskell, Typen, und Typberechnung. Grundlagen der Programmierung 3 A. Einige andere Programmiersprachen. Typisierung in Haskell

Programmierkurs Python I

Software Entwicklung 1. Rekursion. Beispiel: Fibonacci-Folge I. Motivation. Annette Bieniusa / Arnd Poetzsch-Heffter

Datentypen: integer, char, string, boolean

4. Wahrheitswerte. Wo wollen wir hin? Boolesche Werte in der Mathematik. Der Typ bool in C++

Programmierung und Modellierung

Projekt 3 Variablen und Operatoren

Grunddatentypen, Ausdrücke und Variablen Typkonversion, Überprüfen und Auswerten von Ausdrücken

Programmierung WS 2002 / 03: Musterlösung zum 12. Übungsblatt

Grundlegende Datentypen

Informatik I: Einführung in die Programmierung

5.3 Auswertung von Ausdrücken

Informatik I Übung, Woche 38

Haskell, Typen, und Typberechnung. Grundlagen der Programmierung 3 A. Überladung und Konversion in Haskell. Typisierung in Haskell

Programmierkurs Python I

Die Klasse MiniJava ist in der Datei MiniJava.java definiert:

Abschnitt 11: Korrektheit von imperativen Programmen

15. Rekursion. Rekursive Funktionen, Korrektheit, Terminierung, Aufrufstapel, Bau eines Taschenrechners, BNF, Parsen

C.3 Funktionen und Prozeduren

Lösung Probeklausur Informatik I

Programmierung 1 (Wintersemester 2015/16) Wiederholungstutorium Lösungsblatt 15 (Linearer Speicher, Listen, Bäume)

Kapitel 1: Informationsverarbeitung durch Programme

1. Probeklausur Programmierung 1 (WS 2010/2011)

Crashkurs: Haskell. Mentoring FU Berlin Felix Droop

Vorlesung Objektorientierte Programmierung Klausur

Grundlagen der Programmierung 2. Operationale Semantik

Informatik I - Programmierung Globalübung Hugs98 Currying. Hugs98 Currying

Grunddatentypen, Ausdrücke und Variablen Typkonversion, Überprüfen und Auswerten von Ausdrücken

Vorsicht bei redundanten und unvollständigen Matches!

Prüfung Informatik D-MATH/D-PHYS :00 11:00

Grunddatentypen, Ausdrücke und Variablen Typkonversion, Überprüfen und Auswerten von Ausdrücken

Grunddatentypen, Ausdrücke und Variablen Typkonversion, Überprüfen und Auswerten von Ausdrücken

Grundlagen der Programmierung 3 A

Lösungen zum Übungsblatt 10: Entwicklung von Softwaresystemen I (WS 2003/04)

Programmierung WS12/13 Lösung - Übung 1 M. Brockschmidt, F. Emmes, C. Otto, T. Ströder

Dank. Theoretische Informatik II. Teil II. Registermaschinen. Vorlesung

Übungsblatt 8: Software-Entwicklung 1 (WS 2008/09)

Einführung in die Informatik I (autip)

WS 2012/2013. Robert Giegerich. 21. November 2012

Einführung in die Theoretische Informatik

Programmierung 1 (Wintersemester 2015/16) Lösungsblatt: Aufgaben für die Übungsgruppen: 8 (Kapitel 9)

JAVA - Methoden - Rekursion

Institut für Programmierung und Reaktive Systeme. Java 2. Markus Reschke

Kontrollfluss. man Verzweigungen und Sprünge. o bisher linear (von oben nach unten) o Für interessante Programme braucht

Einstieg in die Informatik mit Java

Kapitel 3. Funktionale Programmierung in OCAML. Informatik I WS03/04 Martin Hofmann 90

Allgemeine Hinweise: TECHNISCHE UNIVERSITÄT MÜNCHEN. Name Vorname Studiengang Matrikelnummer. Hörsaal Reihe Sitzplatz Unterschrift

Lösungshinweise/-vorschläge zur Altklausur Abschlussklausur 06/07

Transkript:

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 Lösungen zum Übungsblatt 3: Softwareentwicklung I (WS 2006/07) Aufgabe 1 Operatorpräzedenzen a) fun f (x,y) = ((x + (y * y)) >= x) Der Bezeichner * hat höhere Präzedenz als + und der Bezeichner + hat höhere Präzedenz als >=. b) fun m (a, b) = ((b + (a div b)) = (b mod a)) Die Bezeichner div und mod haben höhere Präzedenzen als + und = und der Bezeichner + hat höhere Präzedenz als =. c) f : int * int bool. Da y und x als Argumente der Operatoren * und + auftretten, und diese vom Typ m : int * int int sind, müssen sowohl x und y als auch die Ausdrücke y * y und (x + (y * y)) den Typ int besitzen. Da die Ausdrücke (x + (y * y)) und x den gleichen Typ besitzen und als Argumente des Infix-Operators >= auftreten, muss der Ausdruck (x + (y * y)) >= x vom Typ bool sein. m : int * int bool. Da a und b als Argumente der Operatoren div und mod auftreten und diese vom Typ int * int int sind, müssen sowohl a und b als auch die Ausdrücke (b + (a div b)) und (b mod a) vom Typ int sein. Da das Resultat der Addition zweier Zahlen vom Typ int vom Typ int ist, muss ebenfalls der Ausdruck (b + (a div b)) den Typ int besitzen. Da die Ausdrücke (b + (a div b)) und (b mod a) den gleichen Typ besitzen und als Argumente des Infix-Operators = auftreten, muss der Ausdruck (b + (a div b)) = (b mod a) vom Typ bool sein. Aufgabe 2 Syntax-, Typisierungs- und Laufzeitfehler a) Liefert das Ergebnis 12.104 : real. b) Liefert einen Typisierungsfehler, da der Ausdruck auf der linken Seite des Gleichheitszeichens vom Typ int ist, der Ausdruck auf der rechten Seite aber den Typ real besitzt. c) Liefert das Ergebnis "reit nie tot ein tier". d) Liefert das Ergebnis true. e) Liefert das Ergebnis false. f) Liefert das Ergebnis true. g) Liefert das Ergebnis true. h) Liefert das Ergebnis false. i) Liefert einen Typisierungsfehler. In dem Ausdruck (fn x => (x + 1))[] wird versucht eine Funktion vom Typ int int auf eine leere Liste anzuwenden. j) Liefert einen Typisierungfehler, da die Ausdrücke 2 und false nicht den gleichen Typ haben. k) Liefert einen Typisierungsfehler, da die Vergleichsoperation < nur für zwei Argumente des gleichen Typs definiert ist. l) Liefert einen Typisierungsfehler, da das erste Argument im if-then-else-konstrukt vom Typ bool sein muß. m) Liefert das Ergebnis "bibabu". n) Liefert das Ergebnis false.

o) Liefert einen Typisierungsfehler, da der Operator not linksassoziativ ist. Der Ausdruck (not not true) wird von links nach rechts gelesen, als wäre er so geklammert: ((not not) true). Hier, in den innersten Klammern, wird versucht die Funktion not auf not anzuwenden, obwohl not eine Funktion ist, die einen Wert vom Typ bool als Argument erwartet. Dagegen liefert der geklammerte Ausdruck: (10 mod 2, true) = (5-(4+1), if true then (not (not true)) else false) das Ergebnis true. p) Liefert einen Deklarationsfehler, da die Variable x nicht deklariert wurde. q) Liefert einen Syntaxfehler, da das Zeichen? kein Bestandteil von Bezeichnern sein darf. r) Liefert das Ergebnis 0.001. s) Liefert einen Typisierungsfehler, da die Gleichheitsoperation = für den Typ real nicht definiert ist. Hinweis: Dagegen liefert der Ausdruck Real.==(1.0e~3, 0.001) das Ergebnis true. t) Liefert das Ergebnis 3.14. Aufgabe 3 Die Datenstruktur String 13 fun dual2decimal (str:string): int = 14 if ((size(str)) = 0) 15 then 0 16 else (if ((substring(str,(size(str))-1,1))="1") 17 then (2*(dual2decimal(substring(str,0,(size(str)) -1)))) + 1 18 else (2*(dual2decimal(substring(str,0,(size(str)) -1)))) 19 ); Aufgabe 4 Rekursion a) Die Funktion fakultaet 29 fun fakultaet n = 30 if n=0 then 1 31 else fakultaet (n-1) * n b) Die Funktion ngeraden 41 fun ngeraden n = 42 if n=0 then [] 43 else ngeraden(n-1) @ [(n-1)*2] c) Die Funktion power 52 fun power (j:int, k:int) : int = 53 if k = 0 then 1 54 else j * power (j, k-1) d) Die Funktion findapprox 63 fun findappr_helper (d:real,n:real,l:real list,i:int) = 64 if i=((length l)-1) then (~1,~1) 65 else if abs(list.nth(l,i)-n) < d 66 then (0,i) 67 else findappr_helper(d,n,l,i+1) 68 69 fun findapprox (d:real, n:real, l:real list) = 70 if null(l) then (~1,~1) 71 else findappr_helper(d,n,l,0) e) Die Funktion log2: 2

80 fun odd n = if n=0 then false 81 else (if n=1 then true 82 else even(n-1)) 83 and even n = if n=0 then true 84 else (if n=1 then false 85 else odd(n-1)) 86 87 fun gerade x = 88 if (x>0.0) 89 then ungerade(x-1.0) - (1.0/x) 90 else 0.0 91 and ungerade x = 92 gerade (x-1.0) + (1.0/x) 93 94 fun log2 n = 95 if even n 96 then gerade (Real.fromInt n) 97 else ungerade (Real.fromInt n) Aufgabe 5 Binärer Zähler 116 fun singletick (i:bool,l:bool list): bool list = 117 if (null(l)) 118 then (if (i=true) 119 then [true] 120 else [] 121 ) 122 else (if (i=true) 123 then (if (hd(l)=false) 124 then (true::(tl(l))) 125 else (false::(singletick(true,tl(l))))) 126 else (if (hd(l)=false) 127 then (false::(tl(l))) 128 else (true::(tl(l)))) 129 ); 130 131 fun tick(l:bool list): bool list = (rev(singletick(true,rev l))); Aufgabe 6 Terminierung a) Die Funktion find1: 140 fun find_helper1(n,e,l) = 141 if e=list.nth(l,n) 142 then n 143 else find_helper1(n+1,e,l) 144 145 fun find1 (e,l) = find_helper1(0,e,l) 146 147 - find1 (3,[]); 148 149 uncaught exception subscript out of bounds 150 raised at: boot/list.sml:47.35-47.44 151 - find1 (3,[2]); 152 153 uncaught exception subscript out of bounds 154 raised at: boot/list.sml:47.35-47.44 155 - find1 (3,[2,4,5,6]); 156 157 uncaught exception subscript out of bounds 158 raised at: boot/list.sml:47.35-47.44 159 - Betrachten wir als Beispiel einen Aufruf auf die Funktion find1 mit dem Parameterwert (3, [2, 4, 5, 6]), find1(3, [2, 4, 5, 6]). Die Auswertung des Ausdrucks find1(3, [2, 4, 5, 6]) verläuft dann folgendermaßen: find1(3, [2, 4, 5, 6]) = find_helper1(0, 3, [2, 4, 5, 6]) = if 3 = List.nth([2, 4, 5, 6], 0) then 0 then find_helper1(1, 3, [2, 4, 5, 6]) = find_helper1(1, 3, [2, 4, 5, 6]) = if 3 = List.nth([2, 4, 5, 6], 1) then 1 then find_helper1(2, 3, [2, 4, 5, 6]) = find_helper1(2, 3, [2, 4, 5, 6]) = if 3 = List.nth([2, 4, 5, 6], 2) then 2 then find_helper1(3, 3, [2, 4, 5, 6]) = find_helper1(3, 3, [2, 4, 5, 6]) = if 3 = List.nth([2, 4, 5, 6], 3) then 3 then find_helper1(4, 3, [2, 4, 5, 6]) = find_helper1(4, 3, [2, 4, 5, 6]) = if 3 = List.nth([2, 4, 5, 6], 4) then 4 then find_helper1(5, 3, [2, 4, 5, 6]) = find_helper1(5, 3, [2, 4, 5, 6]) = if 3 = List.nth([2, 4, 5, 6], 5) then 5 then find_helper1(6, 3, [2, 4, 5, 6]) = find_helper1(6, 3, [2, 4, 5, 6]) = if 3 = List.nth([2, 4, 5, 6], 6) then 6 then find_helper1(7, 3, [2, 4, 5, 6]) = find_helper1(7, 3, [2, 4, 5, 6]) =... 3

Wie man an diesem Beispiel sieht, gibt es Eingaben (e, l) gibt, für welche die Auswertung des Ausdrucks find1(e, l) nicht terminieren kann: Die Auswertung von find1(e, l) startet mit der Auswertung des Ausdrucks find_helper1(0, e, l) und für jeden Auswertungsschritt i gilt, dass Auswertung von find_helper1(i, e, l) mit dem rekursiven Aufruf von find_helper1(i+1, e, l) endet. In der Praxis sieht dies anders aus. Der Aufruf von find1(e, l) terminiert in ML immer. Allerdings wird die Berechnung des Wertes find1(e, l) für bestimmte Eingaben vorzeitig abgebrochen und statt des Ergebnises wird die Ausnahme subscript out of bounds geliefert. Es gibt natürlich auch Ausdrücke, deren Auswertung in ML nicht terminiert. Z.B. in dem unten dargestellten Beispiel terminiert die Auswertung des Bezeichners semaphor nicht. fun semaphoroeffne s = (print "semaphoroeffne\n" ; if s then semaphorschliesse (not s) else false) and semaphorschliesse s = (print "semaphorschliesse\n" ; if s then true else semaphoroeffne (not s) ) val semaphor = semaphoroeffne true; Ein anderer Grund für die Terminierung der Auswertung eines Ausdrucks, der theoretisch nicht terminierend ist, ist die Beschränktheit von Hardwareressourcen, z.b. die Größe des verfügbaren Arbeitsspeichers und der Festplatte. Zum Beispiel verbrauchte die Auswertung des Bezeichners testtliste in dem unten aufgeführten Beispiel schon nach wenigen Minuten 75% des Arbeitsspeichers auf einem standard Desktop-Rechner. Danach hat sich die Auswertung von testliste dramatisch verlangsamt, da die Liste nicht mehr in den frei verfügbaren Platz im Arbeitsspeicher passte und musste immer wieder das Betriebssystem zwischen der Festplatte und Arbeitsspeicher hin und her verschoben werden (sog. swapping). Da der frei verfügbarer Platz auf der Festplatte auch beschränkt ist, ist klar, dass irgendwann die Auswertung von testliste vom Betriebssystem unterbrochen werden müsste. fun f x = x; val meineliste = List.tabulate(1000000,f); val fertig = false fun erzeugeliste (l1,l2) = (print (Int.toString(length(l2))^"\n"); if fertig then l2 else erzeugeliste(l1,(l1@l2))) val testliste = erzeugelist(meineliste,[]); Im Folgenden werden wir formal die Menge A der Eingaben (e, l) mit der Eigenschaft, dass die Auswertung von find1(e, l) theoretisch nicht terminiert, definieren. Die Auswertung von find1(e, l) terminiert nicht, wenn die Eingabe (e, l) eine der folgenden Bedingungen erfüllt. 1. l = [], d.h. e ist beliebig und l ist eine leere Liste. 2. l [] i. 0 i < length(l) e = List.nth(l, i), d.h. e ist beliebig und l ist eine nicht leere Liste und das Element e kommt in der Liste l nicht vor. Da jede leere Liste l auch die Bedingung i. 0 i < length(l) e = List.nth(l, i) erfüllt, reicht es, wenn wir A wie folgt formulieren A = {(e, l) e ist eine ganze Zahl und l ist eine Liste von ganzen Zahlen und i. 0 i < length(l) e = List.nth(l, i) }. b) Die Funktion find2: 171 fun find_helper2(n,e,l) = 172 if (e/list.nth(l,n))<1.0 173 then n 174 else find_helper2(n+1,e,l) 175 176 fun find2 (e,l:real list) = find_helper2(0,e,l) 177 178 - find2(3.0,[]); 179 180 uncaught exception subscript out of bounds 181 raised at: boot/list.sml:47.35-47.44 182 - find2(3.0,[3.4]); 183 val it = 0 : int 184 - find2(3.0,[2.0,3.4]); 185 val it = 1 : int 186 - find2(3.0,[0.0,2.0,3.4]); 187 val it = 2 : int 188 - find2(3.0,[0.0,2.0]); 189 190 uncaught exception subscript out of bounds 191 raised at: boot/list.sml:47.35-47.44 192-4

Es gilt hier die gleiche Überlegung wie in der vorherigen Teilaufgabe. Der Aufruf von find2(e, l) terminiert in ML immer. Allerdings wird die Berechnung des Wertes find2(e, l) für bestimmte Eingaben vorzeitig abgebrochen und statt des Ergebnises wird die Ausnahme subscript out of bounds geliefert. Dies passiert, wenn die Eingabe (e, l) eine der folgenden Bedingungen erfüllt. 1. l = [], d.h. e ist beliebig und l ist eine leere Liste. 2. l [] i e. 0 i < length(l) e = List.nth(l, i) e < e, d.h. e ist beliebig l ist nicht eine leere Liste, in der kein Element größer als e vorkommt. Interesanterweise, stellen hier Listen-Elemente mit dem Wert gleich 0.0, kein Problem dar, da das Ergebnis der Auswertung des Ausdrucks e/0.0 in ML gleich inf für 0 e beziehungsweise inf für 0 > e ist. Es wird dabei keine Ausnahme geliefert. Die Menge A der Eingaben (e, l) mit der Eigenschaft, dass die Auswertung von find2(e, l) theoretisch nicht terminiert, ist wie folgt definiert A = {(e, l) e ist eine reelle Zahl und l ist eine Liste reeller Zahlen und i e. 0 i < length(l) e = List.nth(l, i) e < e }. 5