A állapotgépek (véges automaták) kézenfekvő megvalósítása a táblázatos megvalósítás, melynek lényege, hogy a pillanatnyi állapottól és az aktuális inputtól függően elővesszük egy táblázatból az új állapotot és az állapotátmenethez tartozó tevékenységet megvalósító függvény címét, amit végrehajtunk. Ezzel állapotgépünk algoritmusa így nézhet ki:
Ahogyan az első félévben tanultuk, az állapotgépek (véges automaták) kézenfekvő megvalósítása a [táblázatos megvalósítás](https://infoc.eet.bme.hu/ea12/#13), melynek lényege, hogy a pillanatnyi állapottól és az aktuális inputtól függően elővesszük egy táblázatból az új állapotot és az állapotátmenethez tartozó tevékenységet megvalósító függvény címét, amit végrehajtunk. Ezzel állapotgépünk algoritmusa így nézhet ki:
```
while (van_input) {
új_állapot = állapot_tábla[akt_állapot][input]
tevékenység = [akt_állapot][input]
tevélenység_vegrehajtása
akt_állapot = új_allapot
tevékenység_végrehajtása
akt_állapot = új_állapot
}
```
Így egy konkrét feladat (pl. ly számláló) implementálása csak a két táblázat megfelelő kitöltéséből áll, feltételezve, hogy mind az állapotokat, mind az inputot olyan módon kódoljuk, hogy azzal az adott nyelven lehet táblázatot (tárolót) indexelni. C nyelven az integrál típussal lehet indexelni. C++-ban már más a helyzet (ld. asszociatív tárolók).
A táblázatok (lehet összevont táblázat is) kitöltését segítheti valamilyen generátor, ekkor annak mérete nem nagyon érdekes, de lehet, hogy kézzel töltjük ki, ekkor érdemes a táblázat méretét csökkenteni. A legkézenfekvőbb csökkentési lehetőség, az állapotok összevonása és az inputot csoportokra bontása (ld. ly számláló példa).
Így egy konkrét feladat (pl. ly-számláló) implementálása csak a két táblázat megfelelő kitöltéséből áll, feltételezve, hogy mind az állapotokat, mind az inputot olyan módon kódoljuk, hogy azzal az adott nyelven lehet táblázatot (tárolót) indexelni. C nyelven integrális típussal lehet indexelni. C++-ban már más a helyzet (ld. asszociatív tárolók).
A táblázatok (lehet összevont táblázat is) kitöltését segítheti valamilyen generátor, ekkor azok mérete nem nagyon érdekes. Amennyiben kézzel töltjük ki, érdemes a táblázat méretét csökkenteni. A legkézenfekvőbb csökkentési lehetőség az állapotok összevonása és az inputok csoportokra bontása (ld. ly-számláló példa).
Az alábbiakban bemutatjuk a fenti, táblázatos módszer egy általánosított megvalósítását C++-ban. A feladat ennek megértése és felhasználásával két további állapotgéppel leírható probléma megoldása.
## Állapotgép ősosztály
Az újrafelhasználáshoz készítettünk egy `Allapotgep` ősosztályt(_allapotgep.hpp_), ami a következő template paraméterekkel rendelkezik:
Az újrafelhasználáshoz készítettünk egy `Allapotgep` ősosztályt (_allapotgep.hpp_), amely a következő sablonparaméterekkel rendelkezik:
```c++
template<
typenameAll,//az állapotokat kódoló típus
typenameInp,//az inputcsoportokat kódoló típus
typenameT//az inpu típusa
typenameInp,//az inputcsoportokat kódoló típus
typenameT//az input típusa
>
classAllapotgep{
//...
...
...
@@ -30,24 +31,24 @@ class Allapotgep {
Az állapotgép a genetika szorgalmihoz viszonyítva a template paraméterezés mellett az alábbiakban tér el:
* minden állapotátmenetkor végrehajt egy akciót;
* tárolja a bevezetőben említett 2D táblázatban a következő állapotot és az akciót.
- tárolja a bevezetőben említett táblázatban a következő állapotot és az akciót;
- minden állapotátmenetkor végrehajt egy akciót.
A következő állapot és az akció tárolására heterogén kollekcióként tárolt objektumokat használunk, melynek alaposztálya a Nop osztály. Ez az osztály nem csinál semmit az _akcio_ során, csak eltárolja a következő állapotot.
A következő állapot és az akció tárolására heterogén kollekcióként tárolt objektumokat használunk, melyek alaposztálya a `Nop` osztály. Ez az osztály nem csinál semmit az _akcio_ során, csak eltárolja a következő állapotot.
```c++
structNop{
/// következő állapot
Allkov_allapot;
Nop(Allkov_all):kov_allapot(kov_all){}
Nop(Allkov_all):kov_allapot(kov_all){}
/// ha az állapotgép ebbe az állapotba ér, meghívja ezt a függvényt
/// @param ch - erre az input értékre léptünk ide
virtualvoidakcio(Tch){}
virtualvoidakcio(Tch){}
virtual~Nop(){}
};
```
A 2D táblázathoz egy olyan osztályt definiáltunk, ami az All és Imp sablonparaméterként megadott típusokkal indexelhető, és törli a tárol elemeket, ha elérkezett az idő.
A 2D táblázathoz egy olyan osztályt definiáltunk, amely az All és Imp sablonparaméterként megadott típusokkal indexelhető, és törli a tárolt elemeket, ha elérkezett az idő.
~AllTabla(){// bejárjuk a tárolót és töröljük a dinamikusan létrehozott elemeket
...
...
@@ -60,7 +61,7 @@ A 2D táblázathoz egy olyan osztályt definiáltunk, ami az All és Imp sablonp
}
++i1;
}
};
}
};
```
Az `Allapotgep` osztály továbbá a következő függvényekkel rendelkezik:
...
...
@@ -84,7 +85,9 @@ All operator()(T ch) {
Nézd meg az osztályt tartalmazó _allapotgep.hpp_ fejlécfájlt!
## Lyszamlalo
Az állapotgép használatát egy egyszerű, ismerős feladattal mutatom be: Számoljuk egy szövegben az ly-ok számát! Az állapotgép így *char* bemenetet kap. Az állapotok kódolására az LyAllapot típust, az input csoportok kódolására pedig az LyInput típust vettem fel. Az számláláshoz pedig felvettem egy int számlálót (sz).
Az állapotgép használatát az első félévben már megismert feladattal mutatom be: [Számoljuk egy szövegben az ly-ok számát!](https://infoc.eet.bme.hu/ea12/#6) Gépunk állapottáblája egyszerű:
![ly-számláló](ly.png)
Az állapotgép így *char* bemenetet kap. Az állapotok kódolására az LyAllapot típust, az input csoportok kódolására pedig az LyInput típust vettem fel. Az számláláshoz pedig felvettem egy int számlálót (sz).
_Megjegyzés: használhattunk volna enum helyett olyan osztályt, ami az érkezett karaktertől függően úgy viselkedik, mint az _LyInput_
_Megjegyzés: használhattunk volna enum helyett olyan osztályt, amely az érkezett karaktertől függően úgy viselkedik, mint az _LyInput_
Az `Lyszamlalo`**get()** függvénye visszaadja a megtalált _ly_-ok számát. Továbbá bevezetésre került egy segéd **str()** függvény is. Nézd meg a **runtests** statikus függvényhez tartozó teszteseteket!
Az `Lyszamlalo`**get()** függvénye visszaadja a megtalált _ly_-ok számát. Továbbá bevezettünk egy segéd **str()** függvény is. Nézd meg a **runtests** statikus függvényhez tartozó teszteseteket!
## Feladatok
### Kommentezés
A mintapélda alapján hozz létre egy `Komment` osztályt, ami a bemenetére érkező szabályos C programból kiszűri a /* ... */ alakú kommenteket! Feltételezzük, hogy szövegkonstansban nem szerepel "/\*", ill. "\*/" karaktersorozat. A megszűrt, komment nélküli programot az osztály **std::string get()** tagfüggvényével lehet lekérdezni.
A mintapélda alapján hozz létre egy `Komment` osztályt, amely a bemenetére érkező szabályos C programból kiszűri a /* ... */ alakú kommenteket! Feltételezzük, hogy szövegkonstansban nem szerepel "/\*", ill. "\*/" karaktersorozat. A megszűrt, komment nélküli programot az osztály **std::string get()** tagfüggvényével lehet lekérdezni.
Nézd meg a _main.cpp_-ben lévő teszteket, állítsd az _ELKESZULT_ makrót 1-re és próbáld megoldani a feladatot. A megoldás során a *komment.hpp* és *komment.cpp* fájlokban dolgozz!
Nézd meg a _main.cpp_-ben lévő teszteket, állítsd az _ELKESZULT_ makrót 1-re, és próbáld megoldani a feladatot. A megoldás során a *komment.hpp* és *komment.cpp* fájlokban dolgozz!
### Split
Hozz létre egy `Split` osztályt ami a bemenetére érkező karaktereket úgy dolgozza fel, hogy a konstruktorában megadott karakter mentén feldarabolja és **get()** függvénye egy `std::vector<std::string>` példányt ad vissza a szétválasztott elemekkel!
Hozz létre egy `Split` osztályt, amely a bemenetére érkező karaktereket úgy dolgozza fel, hogy a konstruktorában megadott határoló karakter mentén a szöveget feldarabolja, és **get()** függvénye egy `std::vector<std::string>` példányban adja vissza a szétválasztott elemeket!
A bemenet elején és a végén érkező határoló karaktereket figyelmen kívül hagyjuk. Amennyiben több határolójel érkezik egymás után, azt egy jelnek tekintjük, azat nem keletkezik üres sztring a feldolgozás során.
Nézd meg a _main.cpp_-ben lévő teszteket, állítsd az _ELKESZULT_ makrót 2-re és próbáld megoldani a feladatot. A megoldás során a *split.hpp* és *split.cpp* fájlokban dolgozz!
Nézd meg a _main.cpp_-ben lévő teszteket, állítsd az _ELKESZULT_ makrót 2-re, és próbáld megoldani a feladatot. A megoldás során a *split.hpp* és *split.cpp* fájlokban dolgozz!
## Megoldás
A Git tárolóból letölthető [https://git.ik.bme.hu/Prog2/szorgalmi_feladatok/Allapotgep_2.0](https://git.ik.bme.hu/Prog2/szorgalmi_feladatok/Allapotgep_2.0)
fájlok felhasználásával hozz létre a lokális fejlesztőkörnyezetedben egy C++ projektet! Ehhez felhasználható a *Makefile*, amiben megtalálhatók a fordítási opciók. Tervezz, fejlessz, tesztelj, majd töltsd fel a megoldást a Jporta rendszerbe!
## Beadás
Beadandó a *komment.hpp*, *komment.cpp*, *split.hpp* és *split.cpp* fájlok.
\ No newline at end of file
Beadandó a *komment.hpp*, *komment.cpp*, *split.hpp* és *split.cpp* fájlok.