600 likes | 702 Views
Anwendungsprogrammierung II. Musterlösungen. Aufgabe 1. Beschreiben Sie die Eigenschaften und den Nutzen von Abstrakten Datentypen in Ihren eigenen Worten Lösung: Ein abstrakter Datentyp (ADT) ist durch folgende Eigenschaften charakterisiert: Er exportiert einen Typ
E N D
Anwendungsprogrammierung II Musterlösungen
Aufgabe 1 • Beschreiben Sie die Eigenschaften und den Nutzen von Abstrakten Datentypen in Ihren eigenen Worten • Lösung: • Ein abstrakter Datentyp (ADT) ist durch folgende Eigenschaften charakterisiert: • Er exportiert einen Typ • Er exportiert einen Satz von Operationen • Die Operationen des Interfaces stellen die einzige Möglichkeit dar, auf die Datenstruktur des ADT zuzugreifen • Axiome und Vorbedingungen definieren den Anwendungsbereich der ADT • ADTs bieten eine abstrakte Sicht für die Beschreibung von Eigenschaften von Mengen von Entitäten. Daher ist ihr Gebrauch auch unabhängig von einer bestimmten Programmiersprache.
Aufgabe 2 • Entwerfen Sie eine ADT namens Fraction, die die Eigenschaften von Brüchen und deren Verarbeitung (Berechnung) beschreibt. NICHT CODIEREN ! • Welche Datenstrukturen könnten benutzt werden ? • Wie sieht das Interface aus ? • Nennen Sie einige Axiome und Preconditionen.
Aufgabe 2 - Lösung • ADT Fraction: • (a) Ein einfacher Bruch besteht aus einem Zähler und einem Nenner, beide sind ganzzahlig. Man könnte sowohl ein Array als auch ein record als Datenstruktur verwenden. • (b) Interface: folgende Operationen z.B. sind notwendig: • Zähler bzw. Nenner auslesen • Zähler bzw. Nenner abspeichern • Einen Bruch addieren und die Summe ermitteln • Einen Bruch subtrahieren und die Differenz ermitteln • Multiplikation, Division, etc…… • (c) Folgende Axiome und preconditions sind denkbar: • Der Nenner darf nicht 0 werden, ansonsten ist der Bruch nicht definiert. • Ist der Zähler 0, so hat der Bruch den wert 0, unabhängig vom Wert des Nenners. • Jede Ganzzahl kann durch einen Bruch mit dem Nenner 1 dargestellt werden.
Aufgabe 3 • Testen Sie den folgenden Code mit und ohne der friend Deklaration. Erklären Sie die Fehlermeldung, die auftaucht wenn die friend declaration fehlt. • class Test • { • friend class Friend; • int i; • Test() : i(0) {} • }; • class Friend • { • Test t; • public: • Friend() {} • };
Aufgabe 3 - Lösung • Ohne die friend Deklaration kann das Objekt Test nicht erzeugt werden, da der Standardkonstruktor von Test private ist. Das Problem kann z.B. mit geschachtelten Klassen behoben werden.
Aufgabe 4 • Warum kann man Iteratoren gut als geschachtelte Klassen definieren ? • Antwort: • Iteratoren sind Teil der Abstraktion ihres Container-Objektes (d.h. des ADT‘s, auf das sie sich beziehen). Ein Iterator ohne Container-Objekt ist sinnlos. • Diese Vorgehensweise erlaubt es, daß alle Iteratorklassen einen gemeinsamen Namen haben.
Aufgabe 5 • Erklären Sie kurz in Ihren Worten Nutzen und Wesen der Klassenvorlage. • Antwort: • Klassenvorlagen stellen einen Weg dar, Klassen allgemein so zu definieren, daß beliebige Datentypen unterstützt werden können. • Vorlagen sind sehr nützlich bei der Implementation von generischen Konstrukten wie Vektoren, Stacks, etc., die mit irgend einem beliebigen Datentyp verwendet werden sollen.
Aufgabe 6 • Geben Sie die Syntax für die Instantiierung einer Variablen vec als Instanz einer Klassenvorlage namens vector für 3 int Variablen an. • Antwort: • typedef vector<int,int,int> vec ;
Aufgabe 7 • Gegeben ist die folgende Klassenvorlage in C++: • template < class T > class cell { • protected: • T info; • public: • void set(T x){ info = x; } • T get() { return info; } • }; • Definieren Sie eine Unterklasse namens colored_cell, indem Sie die Klasse cell folgendermassen erweitern: • Eine Feldfarbe color, die die Farbe der Zelle mit einem ASCII Zeichen beschreibt (w für weiss, b für scwarz, r für rot, etc.) • Die Methode set_color (Farbe setzen) • Die Methode get_color (Farbe abfragen) • Eine verbesserte Methode get, die den Inhalt der Zelle zurückgibt, wenn die Farbe nicht weiss ist, und die ansonsten 0 liefert.
Aufgabe 7 - Lösung • template <classT> class colored_cell: public cell<T> • { • protected: • char color; • public: • void set_color(char c){ color = c; } • char get_color() { return color; } • T get() { • if (color != 'w') return info; • else return 0; • } • };
Aufgabe 8 • Schreiben Sie eine Funktionsvorlage für eine Funktion swap, die zwei Argumente beliebigen Typs vertauscht. Testen Sie die Funktion.
Aufgabe 8 - Lösung • #include <iostream> • using namespace std; • template <class T> void MySwap(T& x, T& y) • { • T z = x; • x = y; • y = z; • } • int main() • { • int i1 = 1, i2 = 2; • MySwap(i1, i2); • cout << i1 << " " << i2 << endl; • double f1 = 3.14, f2 = 6.28; • MySwap(f1, f2); • cout << f1 << " " << f2 << endl; • }
Aufgabe 9 • Was versteht man unter “Instantiierung” einer Klassenvorlage ? • Antwort: • Die Benutzung von Klassenvorlagen. Man ersetzt den Platzhalter durch den echten Typbezeichner.
Aufgabe 10 • Codieren Sie die Methoden append(), delFromFront() der Klasse list
Aufgabe 10 – Lösung (append) • virtual void append(const T data) • { • DataNode<T> *p; • p= new DataNode<T>(data); • if (!(_head->right())) _head->right() = p; • if (!(_tail->right())) _tail->right() = p; • _tail=p; • }
Aufgabe 10 – Lösung (delFromFront) • virtual void delFromFront() • { • if(_head->right()!=NULL) _head=_head->right(); • }
Aufgabe 11 • Erzeugen Sie eine Klassenvorlage für eine Klasse VECTOR, die eine Klasse für zweidimensionale Vektoren erzeugt. Der Typ der Vektorelemente soll parametrisiert werden. Implementieren Sie die Methodenaufrufe für die Erzeugung eines neuen Vektors, die Zuweisung der beiden Werte, und zwei Methoden zur Abfrage der x, bzw. y Komponenten des Vektors.
Aufgabe 11 - Lösung • template <class T, class K> class vector • { • public: • Vector(); // Einen neuen Vektor generieren • Setvalues(K x, K y); // Werte zuweisen • K getx(); // X abfragen • K gety(); // y abfragen • private: • K x, K y; // Daten • };
Aufgabe 12 • a) Implementieren Sie die skizzierte Verkettete Liste. Laden Sie die Liste testweise mit den Zahlen 1-10 und zeigen Sie sie an. Implementieren Sie die Methoden putInFront, append, delFromFront, etc. innerhalb der Klasse List. Implementieren Sie die Klassen in folgender Reihenfolge: • class Node • template <class T> class DataNode • template <class T> class ListBase { • template <class Data, class Element> class Iterator { • Hier eine FORWARD Deklaration für List einfügen • template <class T> class ListIterator • template <class T> • class List • b) Testen Sie die Methode delFromFront. Warum wird bei der Anzeige immer noch das erste Element angezeigt (bzw. warum gibt es einen Run-time Fehler) ? Was müsste geändert werden ?
Aufgabe 12 – Lösung (a) I • #include <stdio.h> • #include <stdlib.h> • #include <conio.h> • class Node { • Node *_right; • public: • Node(Node *right = NULL) : _right(right) {} • Node(const Node &val) : _right(val._right) {} • const Node *right() const { return _right; } • Node *&right() { return _right; } • Node &operator =(const Node &val) { right = val._right; return *this; } • const int operator ==(const Node &val) const { • return _right == val._right; } • const int operator !=(const Node &val) const { • return !(*this == val); } • };
Aufgabe 12 – Lösung (a) II • template <class T> • class DataNode : public Node { • T _data; • public: • DataNode(const T data, DataNode *right = NULL) : • Node(right), _data(data) {} • DataNode(const DataNode &val) : • Node(val), _data(val._data) {} • const DataNode *right() const { • return((DataNode *) Node::right()); • } • DataNode *&right() { return((DataNode *&) Node::right()); }
Aufgabe 12 – Lösung (a) III • const T &data() const { return _data; } • T &data() { return _data; } • DataNode &operator =(const DataNode &val) { • Node::operator =(val); • _data = val._data; • return *this; • } • const int operator ==(const DataNode &val) const { • return( • Node::operator ==(val) && • _data == val._data); • } • const int operator !=(const DataNode &val) const { • return !(*this == val); • } • };
Aufgabe 12 – Lösung (a) IV • template <class T> • class ListBase { • public: • virtual ~ListBase() {} // Force destructor to be • // virtual • virtual void flush() = 0; • virtual void putInFront(const T data) = 0; • virtual void append(const T data) = 0; • virtual void delFromFront() = 0; • virtual const T &getFirst() const = 0; • virtual T &getFirst() = 0; • virtual const T &getLast() const = 0; • virtual T &getLast() = 0; • virtual const int isEmpty() const = 0; • };
Aufgabe 12 – Lösung (a) V • template <class Data, class Element> • class Iterator { • protected: • Element _start, _current; • public: • Iterator(const Element start): _start(start), _current(start) {} • Iterator(const Iterator &val): _start(val._start), _current(val._current) {} • virtual ~Iterator() {} • virtual const Data current() const = 0; • virtual void succ() = 0; • virtual const int terminate() const = 0; • virtual void rewind() { _current = _start; } • Iterator &operator =(const Iterator &val) { • _start = val._start; • _current = val._current; • return *this; }
Aufgabe 12 – Lösung (a) VI • const int operator ==(const Iterator &val) const { • return(_start == val._start && _current == val._current); • } • const int operator !=(const Iterator &val) const { • return !(*this == val); • } • }; • template <typename T> class List; • template <class T> • class ListIterator : public Iterator<T, DataNode<T> *> { • public: • ListIterator(const List<T> &list): Iterator<T, DataNode<T> *>(list._head) {} • ListIterator(const ListIterator &val) : Iterator<T, DataNode<T> *>(val) {} • virtual const T current() const { return _current->data(); } • virtual void succ() { _current = _current->right(); } • virtual const int terminate() const { return _current == NULL; }
Aufgabe 12 – Lösung (a) VII • T &operator ++(int) { • T &tmp = _current->data(); • succ(); • return tmp; • } • ListIterator &operator = • (const ListIterator &val) { • Iterator<T, DataNode<T> *>::operator =(val); • return *this; • } • };
Aufgabe 12 – Lösung (a) VIII • template <class T> • class List : public ListBase<T> { • DataNode<T> *_head, *_tail; • public: • List() :_head(NULL),_tail(NULL) {} • List(const List &val) :_head(NULL),_tail(NULL) { • *this = val; • } • virtual ~List() { flush(); } • virtual void flush(){_head->right()=0;}
Aufgabe 12 – Lösung (a) IX • virtual void putInFront(const T data) { • head = new DataNode<T>(data, _head); • if (!_tail) _tail = _head; • } • virtual void append(const T data) { • DataNode<T> *p; • p= new DataNode<T>(data); • if (!(_head->right())) _head->right() = p; • if (!(_tail->right())) _tail->right() = p; • tail=p; • } • virtual void delFromFront() { • if(_head->right()!=NULL) _head=_head->right(); • }
Aufgabe 12 – Lösung (a) X • virtual const T &getFirst() const { • return _head->data(); } • virtual T &getFirst() { return _head->data(); } • virtual const T &getLast() const { • return _tail->data(); } • virtual T &getLast() { return _tail->data(); } • virtual const int isEmpty() const { • return _head == NULL; } • List &operator =(const List &val) { • flush(); • DataNode<T> *walkp = val._head; • while (walkp) append(walkp->data()); • return *this; }
Aufgabe 12 – Lösung (a) XI • const int operator ==(const List &val) const { • if (isEmpty() && val.isEmpty()) return 1; • DataNode<T> *thisp = _head, • *valp = val._head; • while (thisp && valp) { • if (thisp->data() != valp->data()) return 0; • thisp = thisp->right(); • valp = valp->right(); • } • return 1; • } • const int operator !=(const List &val) const { • return !(*this == val); • } • friend class ListIterator<T>; • };
Aufgabe 12 – Lösung (a) XII • void main() • { • int i; • List<int> List1; • List1.putInFront(1); • for (i=10; i<=50; i+=10) List1.append(i); • getch(); • ListIterator<int> LI(List1); • while (!LI.terminate()) { printf("%d\n", LI.current()); LI.succ(); } • List1.delFromFront(); • LI.rewind(); • while (!LI.terminate()) { • printf("%d\n", LI.current()); • LI.succ(); • } • }
Aufgabe 12 (b) • Testen Sie die Methode delFromFront. Warum wird bei der Anzeige immer noch das erste Element angezeigt (bzw. warum gibt es einen Run-time Fehler) ? Was müsste geändert werden ?
Aufgabe 12 – Lösung (b) I • Derzeitiger Code: List1.delFromFront(); LI.rewind(); • Definition von rewind(): virtual void rewind() { _current = _start; } • Definition von delFromFront(): virtual void delFromFront() {if(_head->right()!=NULL) _head=_head->right();}
Aufgabe 12 – Lösung (b) II • Der implementierte Iterator speichert nur ZEIGER auf die Listenelemente. Das bedeutet, daß das Element _start anfänglich auf die Position zeigt, auf die auch _head zeigt. Wenn nun durch rewind der Zeiger _head geändert wird, wird _start nicht ebenfalls geändert. Da das erste Element durch delFromFront nicht physikalisch gelöscht wird, sondern nur Zeiger geändert werden, ändert sich die Ausgabe nicht. Würde das erste Element physikalisch gelöscht werden, so würde ein Laufzeitfehler erzeugt.
Aufgabe 12 – Lösung (b) III • Mögliche Änderung in ListIterator: • template <class T> • class ListIterator : public Iterator<T, DataNode<T> *> { • public: • ListIterator(const List<T> &list) : Iterator<T, DataNode<T> *>(list._head) {} • ListIterator(const ListIterator &val) : Iterator<T, DataNode<T> *>(val) {} • virtual const T current() const { return _current->data(); } • virtual void succ() { _current = _current->right(); } • virtual const int terminate() const { • return _current == NULL; • } • virtual void delFromFront() { rewind(); succ(); } • T &operator ++(int) { • T &tmp = _current->data(); • succ(); • return tmp; • } • ListIterator &operator =(const ListIterator &val) { • Iterator<T, DataNode<T> *>::operator =(val); • return *this; • } • }; _start wird auf das Folgeelement von _start gesetzt
Aufgabe 12 – Lösung (b) IV Ergebnis
Aufgabe 13 • Welche Änderungen müssten in der in Aufgabe 12 einfach verketteten Liste implementiert werden, um eine DOPPELT VERKETTETE LISTE zu erhalten. Doppelt verkettete Listen haben Zeiger sowohl zum vorherigen als auch zum nächsten Knoten. • Antwort: • NODE: Zeiger zum vorherigen Element implementieren • ITERATOR: prev() implementieren
Aufgabe 14 • Implementieren und testen Sie das Programm „Turm von Hanoi“ als C++ Konsolenanwendung. • (siehe Lehrbrief)
Aufgabe 15 • Aus der Mathe-Olympiade 2002: • Gegeben ist folgende Rechnung (jeder Buchstabe stellt eine und • nur eine Ziffer dar): • T I E R • + B A U M • = L E B E N • Die Aufgabe hat mehr als eine Lösung. Schreiben Sie ein Programm in C++, das ermittelt, wie viele Lösungen es gibt und eine .txt Datei mit allen möglichen Lösungen erzeugt. • Hinweise: • Überlegen Sie sich zunächst, wie das Problem LOGISCH zu lösen ist. Extrahieren Sie aus Ihren Überlegungen REGELN, die beim „Probieren“ angewendet werden können. • Das Programm wird (je nach ihrer Effizienz beim Programmieren und der Geschwindigkeit Ihres Rechners) sehr lang laufen. Also Geduld.
Aufgabe 15 – Lösung I (Initialisierung) • #include <stdio.h> • #include <stdlib.h> • #include <console.h> • #include <conio.h> • /*int t,i,e,r; • int b,a,u,m; • int l,e,b,e,n;*/ • int p4,p3,p2,p1; • int a4,a3,a2,a1; • int r5,r4,r3,r2,r1; • char pp[5]="\0",aa[5]="\0",rr[6]="\0";
Aufgabe 15 – Lösung II (Prototypen) • void showvalues(FILE *fp, int p,int a,int r, int l); • void pad(char *s,int n); • int check(int p, int a, int r); • void clrscr(); • void locate(int x, int y); • int check_1(int vr, int b1, int b2,int b3,int b4,int b5,int b6,int b7,int b8,int b9); • void cvt(int p, int a, int r);
Aufgabe 15 – Lösung III (Hauptprogramm) • void main() • { • int a,p,l,r; • FILE *fp; • clrscr(); • fp=fopen("c:\\moly.txt","w"); l=0; • for(p=0; p<=9999; p++) { • locate(5,5); printf("Zaehler %d",p); • for(a=0; a<=9999; a++) { • if (a!=p) { • r=a+p; • if (check(p,a,r)==0) {l++; showvalues(fp,p,a,r,l);} • } • } • } • fclose(fp); • }
Aufgabe 15 – Lösung IV (Konvertierung int -> Array) • void cvt(int p, int a, int r) • { • itoa(p,pp,10); itoa(a,aa,10); itoa(r,rr,10); • pad(pp,4); pad(aa,4); pad(rr,5); • p4=pp[0]-49; p3=pp[1]-49; • p2=pp[2]-49; p1=pp[3]-49; • a4=aa[0]-49; a3=aa[1]-49; • a2=aa[2]-49; a1=aa[3]-49; • r5=rr[0]-49; r4=rr[1]-49; • r3=rr[2]-49; r2=rr[3]-49; • r1=rr[4]-49; • }
Aufgabe 15 – Lösung V (Anzeige und Protokoll) • void showvalues(FILE *fp, int p,int a,int r, int l) • { • cvt(p,a,r); • locate(60,10); printf("%5d",p); • locate(60,11); printf("%5d",a); • locate(60,12); printf("%5d",r); • locate(60,5); printf("Loesung # %d:\n",l); • locate(10,10); printf( " %c %c %c %c",pp[0],pp[1],pp[2],pp[3]); • locate(10,11); printf( " %c %c %c %c",aa[0],aa[1],aa[2],aa[3]); • locate(10,12); printf("%c %c %c %c %c",rr[0],rr[1],rr[2],rr[3],rr[4]); • locate(10,14); printf("T=%c I=%c E=%c R=%c B=%c A=%c U=%c M=%c L=%c N=%c\n",pp[0],pp[1],pp[2],pp[3],aa[0],aa[1],aa[2],aa[3],rr[0],rr[4]); • fprintf(fp,"Loesung # %d:\n",l); • fprintf(fp, " %c %c %c %c\n",pp[0],pp[1],pp[2],pp[3]); • fprintf(fp, " %c %c %c %c\n",aa[0],aa[1],aa[2],aa[3]); • fprintf(fp,"%c %c %c %c %c\n\n",rr[0],rr[1],rr[2],rr[3],rr[4]); • fprintf(fp,"T=%c I=%c E=%c R=%c B=%c A=%c U=%c M=%c L=%c N=%c\n",pp[0],pp[1],pp[2],pp[3],aa[0],aa[1],aa[2],aa[3],rr[0],rr[4]); • fprintf(fp,"===========================\n\n\n"); • }
Aufgabe 15 – Lösung VI (Prüfung auf Gültigkeit) • int check(int p, int a, int r) • { • int c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11; • cvt(p,a,r); • c1=0; c2=0; c3=0; c4=0; c5=0; c6=0; • c7=0; c8=0; c9=0; c10=0; c11=0; • if ((p4==p3) || (p3==p2) || (p2==p1)) { return(1); } • if ((p4==a4) || (p1==a1) || (p3==a3)) { return(1); } • if ((r4==r2) && (r2==p2) && (r4==p2) && (r3==a4)) c1=1; • c2=check_1(p1,p2,p3,p4,a1,a2,a3,a4,r5,r1); • c3=check_1(p2,p1,p4,a1,a2,a3,a3,a4,r5,r1); • c4=check_1(p3,p1,p2,p4,a1,a2,a3,a4,r5,r1); • c5=check_1(p4,p1,p2,p3,a1,a2,a3,a4,r5,r1); • c6=check_1(a1,p1,p2,p3,p4,a2,a3,a4,r5,r1); • c7=check_1(a2,p1,p2,p3,p4,a1,a3,a4,r5,r1); • c8=check_1(a3,p1,p2,p3,p4,a1,a2,a4,r5,r1); • c9=check_1(a4,p1,p2,p3,p4,a1,a2,a3,r5,r1); • c10=check_1(r5,p1,p2,p3,p4,a1,a2,a3,a4,r1); • c11=check_1(r1,p1,p2,p3,p4,a1,a2,a3,a4,r5); • if ((c1==1) && (c2==1) && (c3==1) && (c4==1) && (c5==1) && (c6==1) && (c7==1) && (c8==1) && (c9==1) && (c10==1) && (c11==1)) return(0); • return(1); • }
Aufgabe 15 – Lösung VII (Hildsfunktion f. check(..)) • int check_1(int vr, int b1, int b2,int b3,int b4,int b5,int b6,int b7,int b8,int b9) • { • int ret; • ret=0; • if ((vr!=b1) && (vr!=b2) && (vr!=b3) && (vr!=b4) && (vr!=b5) && (vr!=b6) && (vr!=b7) && (vr!=b8) && (vr!=b9)) ret=1; • return(ret); • }
Aufgabe 15 – Lösung VIII (String mit Blanks auffüllen) • void pad(char *s,int n) • { • int i,j; • if (strlen(s)<n) { • i=strlen(s)-1; • for(j=i;j>=0; j--) { s[n-1-i+j]=s[j]; s[j]='0'; } • for(j=0;j<=n-1;j++) if(s[j]<'0' || s[j]>'9') s[j]='0'; • } • }
Aufgabe 15 – Lösung IX (clrscr) • using namespace std; • POINT screensize; • void clrscr() • { • COORD coordScreen = { 0, 0 }; • DWORD cCharsWritten; • CONSOLE_SCREEN_BUFFER_INFO csbi; • DWORD dwConSize; • HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); • GetConsoleScreenBufferInfo(hConsole, &csbi); • dwConSize = csbi.dwSize.X * csbi.dwSize.Y; • screensize.x = csbi.dwSize.X; • screensize.y = csbi.dwSize.Y; • FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten); • GetConsoleScreenBufferInfo(hConsole, &csbi); • FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten); • SetConsoleCursorPosition(hConsole, coordScreen); • }