-1- Aufgabe 1: (15 Punkte) Eine Tischlerei benötigt zur Herstellung ihrer Regalserie „Freddie“ Bretter der Länge 1 bzw. 2 Meter, welche aus längeren Brettern des Zulieferers zugeschnitten werden müssen. Die Länge der Ausgangsbretter in Metern ist dabei stets ganzzahlig. Schreiben Sie eine Java-Methode static void brettlaengen(int laenge), welche für ein Ausgangsbrett der Länge laenge alle möglichen Zuschnittskombinationen für Bretter der Länge 1 bzw. 2 Meter ausgibt. Die Kombinationen für ein Ausgangsbrett der Länge 3 sind z.B.: 2 1 1 2 1 1 1 Lösung: public static void brettlaengen(int laenge){ brettlaengen("", laenge); } public static void brettlaengen(String kombinationen, int laenge){ if (laenge == 0) System.out.println(kombinationen); if (laenge >= 1) brettlaengen(kombinationen + " 1", laenge - 1); if (laenge >= 2) brettlaengen(kombinationen + " 2", laenge - 2); } -2- Aufgabe 2: (15 Punkte) Gegeben seien die folgenden Haskell-Funktionsdefinitionen: mult2 :: Int -> Int mult2 a = a + a g :: (Int -> Int) -> Int -> Int g f x = f (x * x) Reduzieren Sie den Ausdruck g mult2 2 + g (+ 1) 2 schrittweise! Wenden Sie dabei in jedem Schritt nur eine Reduktionsregel an und dokumentieren sie die jeweiligen Zwischenschritte! In einigen Schritten kommen mehrere Redexe für eine Reduktion in Frage. Gehen Sie in diesen Fällen davon aus, dass der am weitesten links stehende, äußerste Redex zuerst reduziert wird. Beachten Sie, dass Teilausdrücke ggf. mehrfach ausgewertet werden müssen! Lösung: g mult2 2 + g (+1) 2 mult2 (2 * 2) + g (+1) 2 (2*2) + (2*2) + g (+1) 2 4 + (2*2) + g (+1) 2 4 + 4 + g (+1) 2 8 + g (+1) 2 8 + (+1) (2 * 2) 8 + (+1) 4 8 + 5 13 -3- Aufgabe 3: (15 Punkte) Entwickeln Sie in einer Programmiersprache Ihrer Wahl (Haskell oder Java) eine Funktion bzw. Methode, welche alle möglichen Permutationen einer ihr übergebenen Liste paarweise disjunkter, natürlicher Zahlen erzeugt. Die Liste der Permutationen der Liste [1, 2, 3] wäre z.B.: [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] Hinweis: Für eine Implementierung in Java bietet sich die Verwendung von Arrays anstelle von Listen an. Haskell-Lösung: -- man erzeugt alle permutationen einer liste (l:ls), indem man alle -- Permutationen der liste ls erzeugt und l an allen Positionen dieser -- Permutationen einmal einfügt. perm :: [a] -> [[a]] perm [] = [[]] perm (l:ls) = insert l (perm ls) -- fügt ein Element in alle Positionen einer Liste von Listen ein insert :: a -> [[a]] -> [[a]] insert x [] = [] insert x (y:ys) = (map (insert2 x y) [0 .. (length y)]) ++ insert x ys -- fügt insert2 insert2 insert2 insert2 ein Element an eine bestimmte Stelle einer Liste ein :: a -> [a] -> Int -> [a] a [] n = [a] a (y:ys) 0 = (a:(y:ys)) a (y:ys) n = (y:(insert2 a ys (n-1))) oder etwas kürzer: perm :: [a] -> [[a]] perm [] = [[]] perm (x:xs) = [ps ++ [x] ++ qs | rs <- perm xs, (ps, qs) <- splits rs] splits :: [a] -> [([a],[a])] splits [] = [([], [])] splits (y:ys) = ([],y:ys):[(y:ps,qs) | (ps,qs) <- splits ys] -4- Java-Lösung: public static int[][] permutation(int[] liste){ // die erste Permutation im Ergebnis ist die liste selbst int[][] result = new int[1][liste.length]; result[0] = liste; // der rest entsteht durch Vertauschung. Dabei wird jedes Element der // Liste mit jedem der weiter rechts in der Liste stehenden Elementen // einmal vertauscht. for (int left = 0; left < liste.length - 1; left++){ // wie viele Permutation gibt es bereits, aus denen durch // Vertauschungen neue Permutationen entstehen? int anz = result.length; // in den bisherigen Perumtationen das Element an Position left mit // allen rechts danebenliegenden vertauschen und die neuen // Permutationen hinten anhängen for (int right = left + 1; right < liste.length; right++){ for (int zeile = 0; zeile < anz; zeile++){ // die neue Permutation erzeugen int[] newperm = new int[liste.length]; // Reihenfolge aus der alten Permutation übernehmen for (int h = 0; h < newperm.length; h++) newperm[h] = result[zeile][h]; // Elemente left und right vertauschen newperm[left] = result[zeile][right]; newperm[right] = result[zeile][left]; // neue Permutation anhängen result = append(result, newperm); } } } return result; } static int[][] append(int[][] r, int[] a){ int[][] result = new int[r.length + 1][]; for (int i = 0; i < result.length - 1; i++) result[i] = r[i]; result[result.length - 1] = a; return result; } -5- Aufgabe 4: (6+10+8 = 24 Punkte) Eine Versicherungsagentur beschließt im Rahmen eines Forschungsprojektes die Realisierung einiger Teile ihrer Mitarbeiter-, Kunden- und Policenverwaltung durch eine neue, in Java zu implementierende Software zu erproben. Kernkomponenten dieser neuen Software bilden die Klassen Mitarbeiter, Kunde und Police, welche durch eine möglichst genaue Abbildung der in der Realität auftretenden Abhängigkeiten miteinander verknüpft werden sollen. Das Projektteam hat hierfür folgende Abhängigkeiten analysiert: Die Agentur beschäftigt mehrere Mitarbeiter, welche jeweils wiederum für unterschiedliche Kunden zuständig sind. Um ein persönliches Verhältnis zu den Kunden aufbauen zu können, wird jeder Kunde nur von einem Mitarbeiter betreut. Da die Agentur verschiedene Arten von Versicherungen anbietet, kann jeder Kunde mehrere Versicherungspolicen besitzen. Die Provision, welche die Agentur durch die Vermittlung der Versicherungspolice erhalten hat, wird in den Policen selbst festgehalten. Für die erste Version der Software wurden für die einzelnen Klassen folgende Methoden definiert: Klasse Police: • double getProvision() • String getPolicenID() // gibt die Provision der Police aus // gibt das Aktenzeichen der Police aus Klasse Kunde: • void addPolice(Police police) // fügt eine neue Police zu den Policen des // Kunden hinzu • String getName() // gibt den Namen des Kunden zurück • String getAnschrift() // gibt die Anschrift des Kunden zurück • Police[] getPolicen() // gibt ein Array aller Policen den Kunden // zurück Klasse Mitarbeiter: • void addKunde(Kunde kunde) • • String getName() Enumeration getKunden() • void provisionenAusgeben() // fügt einen neuen Kunden in die Liste der // Kunden des Mitarbeiters ein // gibt den Namen des Mitarbeiters aus // Gibt eine Aufzählung aller Kunden des // Mitarbeiters aus // Erzeugt eine Liste der Provisionssummen // pro Kunde des Mitarbeiters a) Stellen Sie die im Text geschilderte Situation durch ein Klassendiagramm dar! b) Implementieren Sie die Klassen Police und Kunde. Achten Sie dabei besonders auf sinnvolle Parameter für die jeweiligen Konstruktoren und eine sichere Kapselung der zu verwaltenden Daten. c) Schreiben Sie die Methode provisionenAusgeben() der Klasse Mitarbeiter, welche eine Auflistung aller Kunden und die Summen der durch Verträge mit ihnen erwirtschafteten Provisionen ausgibt! Hinweis: 1. Die übrigen Methoden der Klasse Mitarbeiter brauchen in dieser Aufgabe nicht implementiert zu werden. 2. Die einzigen Methoden des Interface Enumeration sind: Object nextElement() und boolean hasMoreElements() -6- a) * Kunde name:String anschrift:String policen:Police[] addPolice(police:Police) getName()String getAnschrift():String getPolicen():Police[] 1 Mi tarbeiter name:String 1 kund en:Vect or addKunde(kun de:Kunde) getName():String getKun den():Enumera tion provi sionenAusgeben() Police provi sion:doub le id:String * getProvision ():double getPolicenID():double b) public class Police { protected double provision; protected String id; public Police (String id, double provision){ this.id = id; this.provision = provision; } public double getProvision(){ return provision; } public String getID(){ return id; } } -7- public class Kunde { protected String name; protected String anschrift; protected Police[] policen; public Kunde(String name, String anschrift){ this.name = name; this.anschrift = anschrift; this.policen = new Policen[0]; } public String getName(){ return name; } public String getAnschrift(){ return anschrift; } public void addPolice(Police police){ Police[] tmp = policen; policen = new Police[tmp.length + 1]; for (int i = 0; i < tmp.length; i++) policen[i] = tmp[i]; policen[policen.length – 1] = police; } public Police[] getPolicen(){ return policen; } } c) public static void provisionenAusgeben(){ Enumeration kunden = getKunden(); while (kunden.hasMoreElements()){ Kunde kunde = (Kunde)kunden.nextElement(); double provision = 0; Police[] policen = kunde.getPolicen(); for (int i = 0; i < policen.length; i++) provision += policen[i].getProvision(); System.out.println(kunde.getName() + ": " + provision); } } -8- Aufgabe 5: (10 Punkte) Gegeben sei die folgende Java-Klasse: public class Preis{ public double p; * Name nicht Aussagekräftig private Preis(double p){ p = p; } * Konstruktor ist private * this. fehlt protected static int bruttoPreis{ * static, * int statt double, * Klammer vergessen * Konstanten sollten als solche definiert werden return p + p * 0.16; } public void reduzieren(double p){ * throws … vergessen if (p < 0) throw new InvalidArgumentException(); this.p = this.p * (1 - p); } } public static double sum(Preis[] a){ int i = 1; * Arrayindex startet bei 0 double r; * Variable nicht initialisiert while (i < a.length()){ * Klammern zuviel r = r + a[i]; * a[i] ist kein double sondern ein Objekt. Es fehlt das .p * Schleifenzähler wird nicht erhöht } return r; } Unterstreichen Sie die in dem Quellcode vorhandenen Fehler und dokumentieren Sie die Art des Fehlers in kurzen Stichpunkten. -9- Aufgabe 6: (24 Punkte) Gegeben sei das IMP-Programm P: while n > 0 do (e := e * 2; n := n – 1) Ermitteln Sie die Reduktionssemantik von P in der Umgebung ρ und im Zustand σ, wobei σ (α1 ) = 1 gilt: ρ (n ) = α1 ρ (e) = α 2 σ (α 2 ) = 1 Lösung: Lemma1: e, ρ , σ → 1 2, ρ , σ → 2 n, ρ , σ [α 2 / 2] → 1 1, ρ , σ [α 2 / 2] → 1 e * 2, ρ , σ → 2 n − 1, ρ , σ [α 2 / 2] → 0 e := e * 2, ρ , σ → σ [α 2 / 2] n := n − 1, ρ , σ [α 2 / 2] → σ [α1 / 0, α 2 / 2] e := e * 2; n := n − 1, ρ , σ → σ [α1 / 0, α 2 / 2] Lemma2: n, ρ , σ [α1 / 0, α 2 / 2] → 0 0, ρ , σ [α1 / 0, α 2 / 2] → 0 n > 0, ρ , σ [α1 / 0, α 2 / 2] → false while n > 0 do (e := e * 2; n := n − 1), ρ , σ [α1 / 0, α 2 / 2] → σ [α1 / 0, α 2 / 2] Hauptrechnung: n, ρ , σ → 1 0, ρ , σ → 0 Lemma1 Lemma 2 n > 0, ρ , σ → true while n > 0 do (e := e * 2; n := n − 1), ρ , σ → σ [α1 / 0, α 2 / 2]