Feladat Készítsen egy zsák típust! Alkalmazzon osztályt! A zsákokat rendezett láncolt listával ábrázolja! Implementálja a szokásos műveleteket, egészítse ki az osztályt a kényelmes és biztonságos használat érdekében megfelelő metódusokkal (zsákbeolvasó operátor>>, zsákkiíró operátor<<), alkalmazzon kivételkezelést, készítsen teszt környezetet, és bontsa modulokra a programját! A teszt környezet hívjon meg egy olyan barát-operátort is, amely kiszámítja két zsák unióját (a közös elemek előfordulása összegződik)! Az unióképzés műveletigénye: Ơ(m+n), ahol m és n a két zsáknak megfelelő halmazok elemszáma. Absztrakt megoldás Zsák típus A zsák típus, (szemben a valósággal,) akármennyi elem befogadására képes. Egy láncolt lista osztály örökítésével valósítom meg, ahol az elemek szöveg-szám párokkal vannak reprezentálva. A szöveg személyesíti meg az elemet, a szám meg a multiplicitást. Típus értékhalmaz Szöveg szám párokból alkotott láncolt lista Típus műveletek Módosító műveletek Insert Beletesz az aktuális elem elé egy elemet. Az utolsó után úgy lehet majd beletenni, hogy eggyel tovább kell lépni az utolsó után. Drop Kiveszi az aktuális elemet. Ha az utolsó elem után van a bejárás folyamata, akkor hibát ad. Bejáró műveletek First A tömb első elemére áll. Next A következő elemre lép. Current Az aktuális elem értékét adja vissza. End Visszatérési értéke igaz, hogy ha már túljutottunk az utolsó elemen. last Visszatérési értéke igaz, hogy ha az utolsó elemnél tart. Reprezentáció first Current... nil A láncolt listát egy olyan fejelem nélküli láncolt listával ábrázoljuk, amelyben mindig az aktuális elemnek a mutatóját tároljuk az előző elemből, és módosítjuk azt referenciaként, szükség esetén. A lista elemét az értéke, (value) és a következő elemre mutató mutatóból (ptr) áll. A következő mutatók azonosítják: Head Current Az első elemre mutat. A bejárás aktuális elemére mutat, az előző elem mutatójából referenciaként. 1
Implementáció A bejárás Current változót vezeti végig a listán, amely mindig az előző elem mutatóját tartalmazza referenciaként. Insert Current 1. 2. 1. Az utolsó elem után azért tudunk ezzel a módszerrel beilleszteni, mert a bejáró algoritmus egyel tovább lép a lista hosszánál. Ez később olyan hibát okozhat, amely külön figyelmet igényel! Drop 1. Létrehozzuk a beillesztendő elemet, és a mutatójába írjuk a paraméterezett mutatót. 2. A paraméterezett mutatót átírjuk az új elem mutatójára. 1. 3. 2. Current A függvény azért működhet annak ellenére, hogy fejelem nélküli láncolt listát használunk, mert paraméterként egy mutatót kap, amelyet referenciaként kezelünk. Arra is figyelni kell, hogy ha null referenciát kaphat a függvény! 1. A törlendő elem mutatóját egy segéd változóba helyezzük 2. Töröljük az elemet. 3. A mutatót átírjuk a segédváltozóéval. Megoldás C++-ban A zsák reprezentációját és a láncolt listát magát két részre bontom, hogy könnyebben átlátható legyen a kód, valamint későbbiekben fel tudjam használni az egyik felét. A két rész a láncolt lista szerkezet, a másik meg a zsák reprezentáció. A láncolt listát idő hiánya miatt nem tudtam implementálni magam, így kénytelen voltam áttérni az STL list típusára, viszont rengeteg kód van már meg a saját láncolt listából. Ennek ellenére, lehet, hogy részben, vagy egészbe tartalmaz kódot ebből a saját típusból. 2
Láncolt lista típus A láncolt típust az STL listája mintáján akartam megvalósítani, és ez változó, valamint függvényekben is látszódik. Mindazonáltal bármely típusra működőnek képzeltem el, azaz Templatelve képzeltem el. Az egyik érdekessége ennek a típusnak, hogy fejelem nélküli listát ábrázolna, egy törlő függvénnyel. Ugyanis az előadáson nem így hangzott el. A bejáró műveleteket tartalmazó típust iterátornak neveztem el, és létre kellett hoznom egy konstans változatát is, mindkettőt a publikus részében. A konstans iterátor arra a célra kell, hogy olyan iterátorokat tudjak létrehozni függvényekben, amelyek nem szerkeszthetőek más függvények által, mint például a másoló konstruktorban. A privát részébe egyedül a fej mutató kerül. #ifndef _CHAIN_LIST_HPP #define _CHAIN_LIST_HPP #include <iostream> template<typename T> class clist { private: struct node { T value; node* ptr; node():ptr(0) {; node(node* to):ptr(to) {; *head; public: class iterator { private: node*&that; node*&curr; public: iterator():that(0), curr(0) {; iterator(node*&a):that(a), curr(a) {; iterator(node*&a, node*&b):that(a), curr(b) {; void operator= (const iterator &it) {that=it.that; curr=it.curr; bool operator== (const iterator &it) {return (that==it.that && curr==it.curr); bool operator!= (const iterator &it) {return!(that!=it.that curr!=it.curr); void next() {curr=curr->ptr;; void first() {curr=that; node*¤t() {return curr; bool end() {return (curr==0); bool last() {return (curr==0 curr->ptr==0); ; class const_iterator { 3
private: node* that; node* curr; public: const_iterator():that(0), curr(0) {; const_iterator(node* a):that(a), curr(a) {; const_iterator(node* a, node* b):that(a), curr(b) {; void operator= (const iterator &it) {that=it.that; curr=it.curr; bool operator== (const iterator &it) {return (that==it.that && curr==it.curr); bool operator!= (const iterator &it) {return!(this==it); void next() {curr=curr->ptr;; void first() {curr=that; node* current() {return curr; bool end() {return (curr==0); bool last() {return (curr==0 curr->ptr==0); ; clist():head(0) {; clist(const clist &that); // Copy constructor : should copy the whole list // //#warning: destructor has been remarked! JAL. ~clist(); // Deletes the whole list // // The const keyword and the & chars are for experimental purpose. // void insert(const T &value, node*&item); void insert(const T &value, iterator &it); void drop(node*&item); void drop(iterator &it); /// Iterator based functions /// iterator Begin() {return typename clist<t>::iterator(head); iterator End() {std::cout << head << std::endl; iterator tmp=typename clist<t>::iterator(head); while(!tmp.end()) tmp.next(); return tmp; ;... #endif Konstruktor Tevékenység: Bemenő adatok: Kimenő adatok: Definíció (inline): Egy üres lista létrehozása, amelyet úgy reprezentálunk, hogy a fejmutatót nullára állítunk Nincs Üres lista clist():head(0) {; Másoló konstruktor Tevékenység: Átmásolja a lista elemeit, és hozzá fűzi az éppen létrehozandó listához Bemenő adatok: A másolandó lista Kimenő adatok: A lemásolt lista Definíció: template <typename T> clist<t>::clist(const clist &that) { /// With Iterators /// typename clist::iterator it1(head); 4
typename clist::const_iterator it2(that.head); while (!it2.last()) { it1.current()=new typename clist::node; it1.current()->value=it2.current()->value; it1.next(); it2.next(); Destruktor Tevékenység: Felszabadítja a lista elemeit a memóriában Bemenő adatok: Nem paraméterként önmaga, mint lista Kimenő adatok: Nincs Definíció: template <typename T> clist<t>::~clist() { while (head!=0) { node *tmp; tmp=head; delete head; head=tmp; Láncba fűzés Tevékenység: Befűzni egy láncolt listába egy elemet. Megjegyzés: Két féle befűzés lett létrehozva, ám lehet, hogy nincs szükség mind a kettőre. Bemenő adatok Egy elemmutató referenciaként/egy iterátor referenciaként Kimenő adatok: Az adott elemmel kibővített lista Definíciók: template <typename T> void clist<t>::insert(const T &value, node*&item) { node *tmp=new node(item); tmp->value=value; item=tmp; template <typename T> void clist<t>::insert(const T &value, iterator &it) { node*&ptr=it.current(); node *tmp=new node(ptr); tmp->value=value; ptr=tmp; Láncból törlés Tevékenység: Bemenő adatok: Kimenő adatok: A láncból kiszedi azt az elemet, amelyet egy mutató segítségével adunk meg. Megjegyzés: Ezt a függvényt két féle képen lett definiálva, ám nem biztos, hogy szükség van rá. Egy elemmutató referenciaként/egy iterátor referenciaként A módosított lista 5
Definíciók: template <typename T> void clist<t>::drop(node*&item) { node *tmp; tmp=item->ptr; delete item; item=tmp; template <typename T> void clist<t>::drop(iterator &it) { node*&ptr=it.current(); node *tmp; tmp=ptr->ptr; delete ptr; ptr=tmp; Zsák típus A zsák típus Az előbbi típus köré épül, annak segítségével reprezentálja a zsák tartalmát. Ezen függvényekből csak azokat a függvényeket fogom kihangsúlyozni, amelyeket a feladat kért. Mivel a legtöbb inicializálást, és memória felszabadítást már elvégzi a lista típus, ezért nincs szükség arra, hogy ennél a típusnál külön létrehozzunk egyet. Megjegyzés: Mivel idő híján nem tudtam az előző osztályt befejezni úgy, hogy az működjön, valamint beforduljon ezért az STL könyvtár lista osztályához fordultam. Ezt abból is lehet látni, hogy a felhasznált típus neve nem egyezik az én általam megvalósított osztállyal. Mivel az enyém hasonlít az STL könyvtárban található listával, ezért még így is hasonlít a megvalósítása arra, amit az én osztályommal kellett volna végeznem. #ifndef _LINKED_BAG_HPP #define _LINKED_BAG_HPP //#include "listch.hpp" #include <string> #include <list> class bagins { private: struct multipstr{ std::string value; unsigned int mult; //multipstr():mult(0) {; //multipstr(const multipstr& cp) {value=cp.value; mult=cp.mult;; ; std::list<multipstr> space; public: friend std::istream& operator>> (std::istream &is, bagins &frodo); friend std::ostream& operator<< (std::ostream &os, bagins &bilbo); friend bagins operator+ (bagins &frodo, bagins &bilbo); void put(std::string value); void put(std::string value, int mult); void pop(std::string value); //void pop(std::string value, int mult); 6
; const std::list<multipstr>::iterator at(unsigned int i); unsigned int size() {return space.size(); #endif Beolvasó operátor (jobbra shift) Tevékenység: Beolvas egy megfelelő értéket az alapértelmezett bemenetről, és hozzáadja a zsákhoz. Végén tovább adja a bemenetet tovább olvasáshoz. Bemenet: Adatfolyam, zsák Kimenet: Adatfolyam Definíció: std::istream& operator>> (std::istream &is, bagins &frodo) { string tmp; istream &out=(is >> tmp); frodo.put(tmp); return out; Kiíró operátor (balra shift) Tevékenység: A zsák tartalmát kiírja a kimenő adatfolyamra, és továbbadja az adafolyamot. Bemenet: Adatfolyam, zsák Kimenet: Adatfolyam Definíció: std::ostream& operator<< (std::ostream &os, bagins &bilbo) { stringstream out; for (list<bagins::multipstr>::iterator it=bilbo.space.begin(); it!=bilbo.space.end(); ++it) { if (it!=bilbo.space.begin()) out << ", "; out << it->mult << " x " << it->value; if (out.str()=="") out << "Empty"; return (os << out.str()); Unióképzés (összeadás) Tevékenység: Két zsák tartalmának összeöntése Bemenet: 2 zsák Kimenet: eredmény zsák Definíció: bagins operator+ (bagins &frodo, bagins &bilbo) { bagins out; list<bagins::multipstr>::iterator it=frodo.space.begin(); list<bagins::multipstr>::iterator ut=bilbo.space.begin(); while (it!=frodo.space.end() ut!=bilbo.space.end()) { if (it==frodo.space.end() (it!=frodo.space.end() && it->value>ut->value)) { out.put(ut->value, ut->mult); ++ut; else if (ut==bilbo.space.end() (ut!=bilbo.space.end() && it->value<ut->value)) { out.put(it->value, it->mult); ++it; else if (it->value==ut->value) { out.put(it->value, it->mult+ut->mult); ++it; ++ut; return out; 7
Főprogram A főprogram használ egy olyan sajátkészítésű segédforrást, amely az opciók felsorolását, valamint abból való választást segíti. #include "bag.hpp" #include "ui1.hpp" #include <sstream> using namespace std; using namespace ui1; vector<bagins> bags; void bagcreate(); void putvalue(); void removevalue(); void dropbag(); void displaybags(); void makeunion(); void test(); int main() { vector<string> choice; int answ; bool fool=false; choice.push_back("create bag"); choice.push_back("put value in bag"); choice.push_back("remove value from bag"); choice.push_back("drop bag"); choice.push_back("display bags"); choice.push_back("make two bags union"); choice.push_back("run built-in test"); choice.push_back("quit"); while (!fool) { answ=select(choice); switch(answ) { case 1: bagcreate(); case 2: putvalue(); case 3: removevalue(); case 4: dropbag(); case 5: displaybags(); case 6: makeunion(); case 7: test(); case 8: fool=true; default: cerr << "No such option!" << endl; 8
return 0; void bagcreate() { vector<bagins>::iterator it=bags.begin(); //it.next(); bagins frodo; bags.push_back(frodo); cout << "Bag has created successfully." << endl; int bagselect() { vector<string> bugger; stringstream steam; unsigned int choice; for (unsigned int i=0; i!=bags.size(); ++i) { steam.str(""); steam << "bag : " << bags.at(i); bugger.push_back(steam.str()); do { choice=select(bugger); while (choice > bags.size() choice==0); return choice-1; void putvalue() { if (bags.size()!=0) { cout << "Select a bag to put value in." << endl; int Int=bagSelect(); cout << "Type the value in:" << endl; cin >> bags.at(int); else cout << "Create bag first!" << endl; void removevalue() { if (bags.size()!=0) { cout << "Select a bag to remove a value from." << endl; int Int=bagSelect(); string Cin; cout << "Type the value that should be removed:" << endl; cin >> Cin; bags.at(int).pop(cin); else cout << "Create bag first!" << endl; void dropbag() { if (bags.size()!=0) { cout << "Select a bag to drop." << endl; int Int=bagSelect(); vector<bagins>::iterator it; int i=0; for (it=bags.begin(); it!=bags.end(); ++it) { if (i==int) else ++i; bags.erase(it); else cout << "No bags to remove!" << endl; void displaybags() { int no=1; cout << "Bags in memory:" << endl; for (unsigned int i=0; i!=bags.size(); ++i) { cout << "The content of the " << no++ << ". bag : " << bags.at(i) << endl; if (bags.size()==0) cout << "No bags in memory." << endl; 9
void makeunion() { if (bags.size()!=0) { cout << "Select the first bag." << endl; int first=bagselect(); cout << "Select the second bag." << endl; int second=bagselect(); bagins out; out=(bags.at(first)+bags.at(second)); cout << "These two bags union is:\n" << out << endl; else cout << "Make bag first!" << endl; /// Test functions for test function /// void putvalue(unsigned int bag, string value) { if (bags.size()!=0) bags.at(bag).put(value); void removevalue(unsigned int bag, string value) { if (bags.size()!=0) bags.at(bag).pop(value); void dropbag(unsigned int bag) { if (bags.size()!=0) { vector<bagins>::iterator it; unsigned int i=0; for (it=bags.begin(); it!=bags.end(); ++it) { if (i==bag) else ++i; bags.erase(it); Tesztelési terv A tesztelési elvet használja a tesztelő funkciója is a programnak. Főprogram tesztelése Zsák létrehozása Adat felvétele zsákba Törlés zsákból, létező és nem létező adatot Zsák törlése Unió készítése üres zsákokkal, önmagával, valamint két zsákkal, melyekben előfordul, hogy egyikben vagy másikban van egyező adat, vagy mindkettőben előfordul. Osztály tesztelése A láncolt lista osztály nem készült el! A fentieket következőkkel kiegészítve: Nem létező adat törlése Fejlesztési lehetőségek Az osztály apró hibáinak kicsiszolása, és a program normális befejezése. 10