770 likes | 946 Vues
Ziel 1. Weitere Beiträge zur Wiederverwendbarkeit von Code --> Einführung an Beispielen. Frage: Wie kann man das Hashen von Integern für das Hashen von - komplexen Zahlen - Wörtern - Beschäftigten - ... effizient verwenden?. Ziel 2. Antwort:
E N D
Ziel 1 • Weitere Beiträge zur Wiederverwendbarkeit von Code --> Einführung an Beispielen. • Frage: Wie kann man das Hashen von Integern für das Hashen von - komplexen Zahlen - Wörtern - Beschäftigten - ... effizient verwenden?
Ziel 2 • Antwort: • Verwendung von Templates/Schablonen (bedeutet: • Klassen können als Parameter in Klassen (-schablonen) zur Erzeugung von Klassen verwendet werden.) Durchgeführt für Beispiele.
Gliederung 1 • Neuer Anwendungsbereich • Klasse komplexe Zahlen • Einführung in Templates/Schablonen 1. Am Beispiel (einfache) Liste: • Schablone für (einfache) Liste. • Beispielanwendungen: • Einfache Liste für komplexe Zahlen • Einfache Liste für Integer • Einfache Liste für Beschäftigte • Konsequenzen • Detail: Erzeugung anonymer Klassen
Gliederung 2 2. Beispiel: Hashing allgemein • Annahmen • Liste(nschablone) • Schablone Hashtafel • Vergleich : int <---> Nutzinfo * • Methoden dazu • Beispielanwendungen: • Hashing für komplexe Zahlen • Hashing für Integer • Hashing für Wörter • Hashing für Beschäftigte nach Klassenzugehörigkeit • Hashing für Beschäftigte nach Verdienststufe
Klasse: KomplexeZahl class KomplexeZahl { private: int Re, Im; public: KomplexeZahl (int r, int i) { Re = r; Im = i;} KomplexeZahl * Plus (KomplexeZahl * p); KomplexeZahl * Minus (KomplexeZahl * p); KomplexeZahl * Minus (); KomplexeZahl * Mal (KomplexeZahl * p); void Druck(); bool Gleich (KomplexeZahl * p); }; Liste kompl Zahlen
Komplexe Zahlen • Deute (a, b) als a + i*b. • Operationen sind dann klar. • Beispiel: Multiplikation, Methode Mal KomplexeZahl * Mal (KomplexeZahl * p) { int r, i; r = Re * p->Re - Im * p->Im; i = Im * p->Re + Re * p->Im; return new KomplexeZahl(r, i);} [(a+ib)*(c+id) = (ac-bd) + i(ad + bc) ; ]
Nun aber • Suchverfahren für komplexe Zahlen. • Binäre Suchbäume: nicht möglich, denn • auf der Menge der komplexen Zahlen ist keine Ordnung (direkt) definiert.
Nun aber (Forts.) • Hashing: im Prinzip möglich • Muß also: • Klasse KomplexList zur Formulierung verketteter Listen für komplexe Zahlen definieren, • das Hashing-Verfahren anpassen. • Das ist LANGWEILIG, da i.w. Gleichförmiges wiederholt wird. • Alternative ? --> Klassen als Parameter
Integer_Hashing: Analyse 1 • class IntListenEl { • public: • int Element; • IntListenEl * weiter; • void NutzinfoDruck()}; • class IntListe { • protected: • IntListenEl * DieIntListe; • bool IstDa(IntListenEl *, int); • IntListenEl * WegDamit (IntListenEl *, int); • void DieserDruck(IntListenEl *);
Integer_Hashing: Analyse 2 • // class IntListe Fortsetzung • public: • IntListe() • IntListe(int r) • bool Testen(int r) • void Entfernen(int r) • void Einfuegen(int) • void Druck()}; • .... • bool IntListe::IstDa(IntListenEl * Liste, int r) { • ....... • else { if (Liste->Element == r) Gleichheit???!!! • ........ Int-Hash: Analyse 3
Analyseergebnis • Analyse der Rolle der int-Größe (vorige Folien) • Ganze Zahlen gehen bei der KlasseIntListenur insofern ein, als • die Komponente Element vom Typint war, • beim Testen die Gleichheit ganzer Zahlen benutzt wurde • sie als Parameter in Methoden auftreten --> Diese Stellen müssen bei einer abstrakteren Behandlung ("Parametrisierung") auf Änderung überprüft werden.
Aufgabe: Abstrakte Formulierung • Listentyp definieren, der diese beiden Angaben (Typ, Gleichheit) als Parameter hat: • ganze Zahlen als Typparameter, • die Gleichheit als Funktionsparameter (Das geht mit Hilfe rein virtueller Funktionen. Wird zunächst zurückgestellt).
Dazu zu erledigen: • Allgemeine Angaben zu Liste: • Annahme über den Typ der Listenelemente, • Angabe der Signaturen von Listenmethoden, • Angabe • der Implementierung, soweit das möglich ist • der typspezifischen Eigenschaften oder Operationen. • Formulierung in C++ • [Zunächst Reduktion auf Liste_einfach: • Initialisieren der Liste - Einfügen eines Elements • Drucken der Liste(nelemente) ]
Nutzinfo weiter Erste Formulierungen • Annahme: • Der Typ der Listenelemente ist Zeiger auf Nutzinfo, wobei Nutzinfo beliebige Klasse. • Grundtyp also: Nutzinfo *. Nutzinfo • class ListenEl { • Nutzinfo * Element; • ListenEl * weiter; • void NutzinfoDruck(){ • Element->Druck(); cout << endl;}}; Element Achtung: Setzt Methode Druck() in Nutzinfo voraus !!!
Erste Formulierungen 2 • class Liste { • ListenEl * DieListe; • void ListenDruck(ListenEl *); • Liste() { • DieListe = NULL;}; • void Einfuegen(Nutzinfo *); • void Druck(){ListenDruck(DieListe);}
Fazit • Fast alle Operationen ohne Kenntnisse des zugrundeliegenden Typs Nutzinfo formulierbar. • Ausnahmen: • der Druck eines Elements • evtl: Gleichheit von Elementen (hier (noch) nicht betrachtet) • Im folgenden: Formulierung als Klasse
Formulierung als Klasse 1 • Dazu auch berücksichtigen: • Angabe, daß der Typ eine Art Parameter der Listenklasse ist !! • Zugriffsspezifikationen (private, protected, public) • Methoden müssen ausformuliert werden
Erste Formulierungen (Ausschnitt) class ListenEl { • Nutzinfo * Element; • ListenEl * weiter; • void NutzinfoDruck(){ • Element->Druck(); • cout << endl; • } • };
Das ist neu Formulierung als Klasse 2 template <class Nutzinfo> • class ListenEl { • public: • Nutzinfo * Element; • ListenEl * weiter; • void NutzinfoDruck(){ • Element->Druck(); • cout << endl; • } • };
Anmerkungen • Durch template <class Nutzinfo >wird angekündigt, daß die folgende Konstruktion eine Klasse Nutzinfo als Parameter hat. • Die nun folgende Klasse ist keine: • Sie ist vielmehr das Muster (oder die Schablone) einer Klasse. Aus der Schablone können dann Klassen erzeugt werden (Wie, werden wir gleich sehen). • Schablonen können im Hinblick auf Vererbung wie Klassen behandelt werden.
Anmerkungen (Forts.) • Zugriffsspezifikationen • Element; • weiter; • NutzinfoDruck() als public Komponenten, da wir (nur an dieser Stelle !!) faul sind. Logisch wäre protected oder gar private besser. Vgl. Vorgehen in früheren Beispielen.
Erste Formulierungen 2 (Wiederholung) • class Liste { • ListenEl * DieListe; • void ListenDruck(ListenEl *); • Liste() { • DieListe = NULL;}; • void Einfuegen(Nutzinfo *); • void Druck(){ListenDruck(DieListe);}
Auch bei Parametern oder lokalen Variablen muß der Typparameter stets mitgeteilt werden. Formulierung als Klasse 3 • template <class Nutzinfo> • class Liste { • protected: • ListenEl<Nutzinfo> * DieListe; • void ListenDruck(ListenEl<Nutzinfo> *); • public: • Liste() { • DieListe = NULL;}; • void Einfuegen(Nutzinfo *); • void Druck(){ListenDruck(DieListe);} • };
Realisierung der Methoden 1 • ListenEl<Nutzinfo> * • neu = new ListenEl<Nutzinfo>; template <class Nutzinfo> mußvor der Definition jeder Methode stehen Der Name des Typparameters muß bei der Angabe der Klasse genannt werden template <class Nutzinfo> void Liste<Nutzinfo>::Einfuegen(Nutzinfo * r){ neu->Element = r; neu->weiter = DieListe; DieListe = neu;} Auch hier: Typparameter
Realisierung der Methoden 2 • template <class Nutzinfo> • void Liste<Nutzinfo>:: • ListenDruck(ListenEl<Nutzinfo> * K) { • if (K != NULL) { • K->NutzinfoDruck(); • ListenDruck(K->weiter); • } • }
Realisierung der Methoden 3 • template <class Nutzinfo> • void Liste<Nutzinfo>::Einfuegen(Nutzinfo * r){ • ......} • Die Konstruktion müßte jetzt klar sein: • Angabe, daß es sich um eine Schablone mit Typparameter Nutzinfo handelt, • Nennung des Typparameters bei der Definition der Methode (hier: Einfuegen). Liste_einfach.cpp
Anwendung: Liste komplexer Zahlen • class KomplexL: public Liste<KomplexeZahl> { • public: • KomplexL() : Liste<KomplexeZahl>() {};}; int main() { KomplexL * MeineKoZListe = new KomplexL(); for (int i = 0; i< 10;i++){ MeineKoZListe-> Einfuegen(new KomplexeZahl (rand()%100, rand()%100));}; MeineKoZListe->Druck();return 0;} Quelltext Komplexe Zahlen Programm
Anwendung: Liste von Integer-Zahlen 1 • Erzeuge Klasse zu Integer-Zahlen: • class IntegerZahl { • public: • int i; • IntegerZahl (int i1) { i = i1;} • void Druck() { cout << i; } • int Get_i() {return i;} • bool Gleich(IntegerZahl *p) { • return (i == p->i);} • }; Hashing von Integern Liste von Integern
Anwendung: Liste komplexer Zahlen Wdh • class KomplexL: public Liste<KomplexeZahl> { • public: • KomplexL() : Liste<KomplexeZahl>() {};}; int main() { KomplexL * MeineKoZListe = new KomplexL(); for (int i = 0; i< 10;i++){ MeineKoZListe-> Einfuegen(new KomplexeZahl (rand()%100, rand()%100));}; MeineKoZListe->Druck();return 0;}
Anwendung: Liste von Integer-Zahlen 2 • class IntL: public Liste<IntegerZahl> { • public: • IntL() : Liste<IntegerZahl>() {};}; int main() { IntL * MeineIntListe = new IntL(); for (int i = 0; i< 10;i++){ MeineIntListe-> Einfuegen(new IntegerZahl (rand()%100));}; MeineIntListe->Druck();return 0;} Quelltext Integer Programm
Anwendung: Liste von Beschäftigten • class BeschL: public Liste<Beschaeftigter> { • public: • BeschL() : Liste<Beschaeftigter>() {};}; int main() { BeschL * MeineBeschListe = new BeschL(); ..... MeineBeschListe-> Einfuegen(einBeschaeftigter); .......... MeineBeschListe->Druck();return 0;} Quelltext Klasse Beschäftigter Programm
Anmerkungen • Die neue Klasse entsteht durch Konkretisie-rung des Typparameters. Das geschieht hier mittels Vererbung: class IntL: public Liste<IntegerZahl> { • ........ }; • Die (konkrete) Klasse Liste<IntegerZahl> entsteht dabei aus der Klasse(nschablone) Liste < Nutzinfo>, indem der Typparameter Nutzinfo durch IntegerZahl substituiert wird.
Konsequenzen • Code für Listen wurde nur einmal formuliert • Für jeden Typ kann mit dieser Technik eine Liste formuliert werden. • Dazu muß lediglich formuliert werden: • ein Konstruktor, • // die virtuelle Methode ELDruck, die typspezifisch ist. • Dies geschieht hier durch Vererbung.
Konsequenzen (Forts.) • Für jeden konkreten Typ, der den Typpara-meter ersetzt, wird bei der Ersetzung eine Klasse erzeugt (s.o.). • Diese kann im wesentlichen wie jede andere Klasse verwendet werden. • „Anonyme“ Klasse • Die Ersetzung des Typparameters kann auch dort geschehen, wo Klassen benutzt werden. --> vgl. Beispiel nächste Folien.
Anwendung: Liste komplexer Zahlen UKlasse • class KomplexL: public Liste<KomplexeZahl> { • public: • KomplexL() : Liste<KomplexeZahl>() {};}; int main() { KomplexL * MeineKoZListe = new KomplexL(); for (int i = 0; i< 10;i++){ MeineKoZListe-> Einfuegen(new KomplexeZahl (rand()%100, rand()%100));}; MeineKoZListe->Druck();return 0;}
Anwendung: Liste komplexer Zahlen anonym • /*class KomplexL: public Liste<KomplexeZahl> { • public: • KomplexL() : Liste<KomplexeZahl>() {};}; */ Programm int main() { Liste<KomplexeZahl> * MeineKoZListe = new Liste<KomplexeZahl>; for (int i = 0; i< 10;i++){ MeineKoZListe-> Einfuegen(new KomplexeZahl (rand()%100, rand()%100));}; MeineKoZListe->Druck();return 0;}
Gliederung 1 • Neuer Anwendungsbereich • Klasse komplexe Zahlen • Einführung in Templates/Schablonen 1. Am Beispiel (einfache) Liste: • Schablone für (einfache) Liste. • Beispielanwendungen: • Einfache Liste für komplexe Zahlen • Einfache Liste für Integer • Einfache Liste für Beschäftigte • Konsequenzen • Detail: Erzeugung anonymer Klassen Erledigt !!
Gliederung 2 2. Beispiel: Hashing allgemein • Annahmen • Liste(nschablone) • Schablone Hashtafel • Vergleich : int <---> Nutzinfo * • Methoden dazu • Beispielanwendungen: • Hashing für komplexe Zahlen • Hashing für Integer • Hashing für Wörter • Hashing für Beschäftigte nach Klassenzugehörigkeit • Hashing für Beschäftigte nach Verdienststufe
Hashing für beliebige Datentypen • Hashing (Wiederholung): • Bisher formuliert für ganze Zahlen. • Zentral: Feld von Listen ganzer Zahlen, in die eingefügt wurde. • Feldelement wurde ermittelt durch Hashfunktion.
Idee Hashfunktion (vgl. Kap. 18) • Formal: • Sei U die (endliche) Menge aller Objekte, für die die Suchoperationen definiert werden sollen. • h: U -> {0, ..., m-1} sei eine Funktion --> Hash-Fkt. • U‘: Menge aller Elemente, die bereits verarbeitet sind. • U‘(j) := h -1(j) U‘: Menge aller Elemente in U‘, die von h auf j abgebildet werden. • Aufgabe: t U schon in U‘ ?
0 1 2 3 4 Bildchen Listen das Feld
Hashing Verwendung • • Benutze die verketteten Listen, um: • Element t wird in der Liste zu h(t) abgespeichert. • Suche t? Schaue in der Liste zu h(t) nach. • Lösche t? Lösche t in der Liste zu h(t). • • Zentral: Hashfunktion h • Muß einfach zu berechnen sein • Sollte die Elemente (möglichst) gleichmäßig verteilen.
Zur Schablone für Listen mit Typ Nutzinfo • Annahmen: • Für Objekte des Typs Nutzinfo ist eine Druck-Methode definiert. • Also: für qvom TypNutzinfo ist q.Druck() aufrufbar. • Objekte des Typs Nutzinfo können verglichen werden: • Fürp vom Typ Nutzinfo * und q vom Typ Nutzinfo* ist p->Gleich(q) definiert und gibt true oder false zurück.
Listen- Schablone 1 • template <class Nutzinfo> • class ListenEl { • public: • Nutzinfo * Element; • ListenEl * weiter; • void NutzinfoDruck(){ • Element->Druck(); • cout<<endl; • } • };
Listen- Schablone 2 • template <class Nutzinfo> • class Liste { • protected: • ListenEl<Nutzinfo> * DieListe; • bool IstDa(ListenEl<Nutzinfo>*, Nutzinfo*); • /* Testet, daß Nutzinfo in Liste */ • ListenEl<Nutzinfo> * WegDamit • (ListenEl<Nutzinfo> *, Nutzinfo *); • /* Entfernt Nutzinfo, falls in Liste • enthalten*/ • void ListenDruck(ListenEl<Nutzinfo> *);
Listen- Schablone 3 • //class ListeFortsetzung • public: • Liste() {DieListe = NULL;}; • Liste(Nutzinfo * r) { • DieListe -> Element = r; • DieListe -> weiter = NULL;}; • bool Testen(Nutzinfo * r) { • return IstDa(DieListe, r);} • void Entfernen(Nutzinfo * r) { • DieListe = WegDamit(DieListe, r);} • void Einfuegen(Nutzinfo *);
Listen- Schablone 4 • //class ListeFortsetzung 2 • void Druck(){ListenDruck(DieListe);} • int Gleich(Nutzinfo * p, Nutzinfo * q){ • return p->Gleich(q);} • /* In Nutzinfo muß also • Gleich(Nutzinfo *)definiert sein.*/ • int Laenge();
Methode IstDa • template <class Nutzinfo> • bool Liste<Nutzinfo>::IstDa( • ListenEl<Nutzinfo> * K, Nutzinfo * r) { • if (K == NULL) • return false; • else { • if (Gleich(K->Element, r)) • return true; • else • return IstDa(K->weiter, r); • } • }
Methode Einfuegen • template <class Nutzinfo> • void Liste<Nutzinfo>::Einfuegen(Nutzinfo * r) { • if(!Testen(r)) { • ListenEl<Nutzinfo> * neu • = new ListenEl<Nutzinfo>; • neu->Element = r; • neu->weiter = DieListe; • DieListe = neu; • }} • Zu Restlichen vgl. Implementierung im Quelltext Hash-List.cpp