Autor: Krzysztof
ZPO 2018: Zadanie przed lab.4
Zamieszczony poniżej kod programu reprezentuje przykład programowania z wykorzystaniem cech charakterystycznych. W programie wykorzystywane są dwie klasy reprezentujące dwa pojęcia:
temperatura_wody
– liczba rzeczywista reprezentująca temperaturę wodykostka_do_gry
– liczba całkowita reprezentująca wartość, jaka może wypaść w wyniku rzutu kostką do gry.
Dla tych klas zdefiniowano ich cechy charakterystyczne. Są nimi:
- _jest_liczba – cecha mówiąca, czy danych obiekt reprezentuje liczbę,
- _jest_liczba_calkowita – cecha mówiąca, czy liczba którą reprezentuje danych obiekt, jest liczbą całkowitą,
- _nalezy_do_przedzialu – cecha mówiąca, czy liczba którą reprezentuje danych obiekt, należy do przedziału,
- _dolna_granica_przedzialu – wartość dolnej granicy przedziału, do którego mogą należeć liczby reprezentowane przez danych obiekt,
- _gorna_granica_przedzialu – wartość górnej granicy przedziału, do którego mogą należeć liczby reprezentowane przez danych obiekt,
Cechy charakterystyczne zostały zaimplementowane w programie. W tym celu najpierw zostały zaimplementowane dwie klasy: Bazowe_Cechy
oraz Cechy: public Bazowe_Cechy
tworzące pojęcie zbioru cech.
Następnie dla każdej z dwóch klas: temperatura_wody
i kostka_do_gry
zostały skonkretyzowane w odpowiedni sposób przypisane im klasy cech:
Cechy<temperatura_wody>: public Bazowe_Cechy
Cechy<kostka_do_gry>: public Bazowe_Cechy
Działanie programu polega na gromadzeniu danych liczbowych różnych typów w kontenerach, przy czym kontenery do walidacji wprowadzanych danych używają cech charakterystycznych. W tym celu zdefiniowana została klasa reprezentująca kontener SzablonStosu
przystosowany do przechowywania dowolnych wartości, w tym obiektów typu temperatura_wody
i kostka_do_gry
. Metoda push
tego kontenera przed umieszczeniem danej dokonuje jej walidacji posługując się informacjami z klasy Bazowe_Cechy
.
Wykorzystanie kontenera zostało zademonstrowane w funkcji main
. W kodzie main
tworzone są trzy kontenery K1, K2 i K3, a następnie są wypełniane wartościami. Uwaga: kontener K1 jest zapełniany wartościami tak długo, póki wystarczy zasobów komputera (w trakcie wykonania programu warto uruchomić menedżer zadań i w sekcji wydajności obserwować, jak ubywa wolnej pamięci w miarę pracy programu).
Zadania do zrobienia przed ćwiczeniami
- Przenieś kod do środowiska VS C++, skompiluj i uruchom.
- Przeanalizuj kod, upewnij się, że rozumiesz rolę wszystkich pól i metod w klasach i potrafisz to wyjaśnić na zajęciach.
- Upewnij się, że potrafisz wyjaśnić, dlaczego tyle właśnie elementów zostaje umieszczonych w każdym z trzech kontenerów (a nie więcej).
- dodaj do kodu jeszcze jedną klasę reprezentującą pojęcie liczbowe „numer kołnierzyka koszuli” i skonkretyzuj w odpowiedni sposób odpowiadającą jej klasę cech. Następnie, jeżeli to konieczne, rozszerz kod metody
push
tak aby poprawnie walidował obiekty nowego typu. W funkcjimain
dodaj kontener K4 w którym zgromadzisz kilka obiektów nowego typu. - Przygotuj się do rozwijania tego kodu dla potrzeb nowych rodzajów wartości, jakie można przechowywać w tym kontenerze.
Pliki do pobrania
Plik programu: ZPO-lab4.cpp
Plik z danymi wejściowymi: qv-lab4 (po założeniu nowego projektu VC++ należy go umieścić w tym samym folderze, co plik z kodem programu).
/* ========================================
ZPO: laboratorium 4
WMP.SNS UKSW, Warszawa
========================================
*/
#include<iostream>
#include<fstream>
#include<string>
#include<limits>
#include<exception>
using namespace std;
class Bazowe_Cechy {
public:
static const bool _jest_liczba = false;
static const bool _nalezy_do_przedzialu = false;
static const bool _jest_liczba_calkowita = false;
static const int _dolna_granica_przedzialu = 0;
static const int _gorna_granica_przedzialu = 0;
};
template<typename T>
class Cechy : public Bazowe_Cechy {
public:
static const double dolna_granica_przedzialu() { return 0; };
static const double gorna_granica_przedzialu() { return 0; };
};
class temperatura_wody {
double t;
public:
temperatura_wody(double temp = 50) : t(temp) {};
double operator()() { return t; };
temperatura_wody& operator=(double temp) { t = temp; return *this; };
};
template<>
class Cechy<temperatura_wody> : public Bazowe_Cechy{
public:
static const bool _jest_liczba = true;
static const bool _nalezy_do_przedzialu = true;
static const double dolna_granica_przedzialu() { return 0; };
static const double gorna_granica_przedzialu() { return 100; };
};
class kostka_do_gry {
int n;
public:
kostka_do_gry(int num = 1) : n(num) {};
int operator()() { return n; };
kostka_do_gry& operator=(int num) { n = num; return *this; };
};
template<>
class Cechy<kostka_do_gry> : public Bazowe_Cechy{
public:
static const bool _jest_liczba = true;
static const bool _nalezy_do_przedzialu = true;
static const bool _jest_liczba_calkowita = true;
static const int _dolna_granica_przedzialu = 1;
static const int _gorna_granica_przedzialu = 6;
};
class Przepelnienie : public exception {
char opis[100];
public:
Przepelnienie(const char* o) { strcpy_s(opis, o); }
const char* what() const throw() { return opis; };
};
class BrakDanych : public exception {
char opis[100];
public:
BrakDanych(const char* o) { strcpy_s(opis, o); }
const char* what() const throw() { return opis; };
};
template<typename T, int rozmiar, class _Cechy = Cechy<T>>
class SzablonStosu{
T stos[rozmiar];
int top;
public:
int zajetosc() { return top; };
SzablonStosu() : top(0) {}
void push(const T& i) {
if (top == rozmiar)
throw Przepelnienie(typeid(i).name());
stos[top++] = i;
}
void push(int i) {
if (top == rozmiar)
throw Przepelnienie(typeid(i).name());
// walidacja wartości przekazanej do zapisu
if (Cechy<T>::_jest_liczba && Cechy<T>::_jest_liczba_calkowita) {
if (Cechy<T>::_nalezy_do_przedzialu) {
if ((Cechy<T>::_dolna_granica_przedzialu <= i) && (i <= Cechy<T>::_gorna_granica_przedzialu))
stos[top++] = i;
}
else
stos[top++] = i;
}
}
void push(double i) {
if (top == rozmiar)
throw Przepelnienie(typeid(i).name());
// walidacja wartości przekazanej do zapisu
if (Cechy<T>::_jest_liczba && !Cechy<T>::_jest_liczba_calkowita) {
if (Cechy<T>::_nalezy_do_przedzialu) {
if ((Cechy<T>::dolna_granica_przedzialu() <= i) && (i <= Cechy<T>::gorna_granica_przedzialu()))
stos[top++] = i;
}
else
stos[top++] = i;
}
}
T pop() {
if (top == 0)
throw BrakDanych(typeid(stos[0]).name());
return stos[--top];
}
};
int main() {
SzablonStosu<string, 5> K1;
SzablonStosu<temperatura_wody, 10> K2;
SzablonStosu<kostka_do_gry, 10> K3;
// zapełnianie stosu
ifstream fi("qv-lab4.txt");
string s;
try{
K1.push("Henryk");
K1.push("Sienkiewicz");
while (fi) {
fi >> s;
K1.push(s);
fi.seekg(ios::beg);
fi.clear();
cout << '*';
};
}
catch (Przepelnienie& e){
cout << "K1 gotowy: " << e.what() << endl;
};
cout << "Danych na stosie K1: " << K1.zajetosc() << endl;
K2.push(temperatura_wody());
K2.push(temperatura_wody(36.6));
K2.push(40);
K2.push(71.2);
cout << "Danych na stosie K2: " << K2.zajetosc() << endl;
K3.push(kostka_do_gry(3));
K3.push(kostka_do_gry());
K3.push(4);
K3.push(6);
K3.push(10);
cout << "Danych na stosie K3: " << K3.zajetosc() << endl;
// opróżnianie stosu
try{
while (true)
K1.pop();
}
catch (BrakDanych& e) {
cout << "K1 pusty: " << e.what() << endl;
}
try{
while (true)
K2.pop();
}
catch (BrakDanych& e) {
cout << "K2 pusty: " << e.what() << endl;
}
try{
while (true)
K3.pop();
}
catch (BrakDanych& e) {
cout << "K3 pusty: " << e.what() << endl;
}
system("pause");
return 0;
}
ZPO 2018: Wykład 6 – slajdy
ZPO 2018: Wykład 5 – slajdy
ZPO 2018: Zadanie przed lab.3
Zadanie, które dotyczy zakresu materiału, jaki będzie poruszany na lab. 3. Rozwiązanie nie jest wymagane. Zadanie stanowi wyłącznie pomoc w przygotowaniu się do zajęć.
Zadanie:
Korzystając z szablonu kontenera Konten utwórz w funkcji main
obiekt reprezentujący kontener do przechowywania zmiennych typu double
i umieść w nim za pomocą metody push
dowolną liczbę (nie większą od 100) wartości losowych rzeczywistych z przedziału 〈0, 10).
Napisz szablony trzech funkcji, które jako argument wejściowy przyjmują parę iteratorów identyfikujących przedział danych z kontenera, i które dla liczb w tym przedziale znajdują lub obliczają i zwracają:
- wartość minimalną/maksymalną,
- medianę,
- wartość średnią.
Przyjmij, że typ danych przechowywanych w kontenerze zawsze pozwala na wszystkie standardowe operacje arytmetyczne i logiczne (posiada odpowiednie operatory). Uwaga: funkcje nie otrzymują w argumentach wywołania referencji ani wskaźnika do obiektu kontenera, a jedynie iteratory wskazujące na poprawny przedział danych w nim zawartych.
Zademonstruj poprawne działanie tych szablonów funkcji wywołując je w funkcji main
dla przykładowych danych zapisanych w kontenerze. Na początku na potrzeby testów poprawności spreparuj dane testowe w kontenerze tak, aby łatwo było w pamięci policzyć wynik i potwierdzić poprawność działania szablonów funkcji.
Hint: aby wygenerować wartość losową z przedziału 〈0, 1) można np. użyć funkcji:
//generates a psuedo-random double between 0.0 and 0.999...
double randdouble()
{
return rand()/(double(RAND_MAX)+1);
}
ZPO 2018: Wykład 4 – slajdy
ZPO 2018: Wykład 3 – slajdy
ZPO 2018: Zadania przed lab.2
Zadania, które dotyczą zakresu materiału, jaki będzie poruszany na lab. 2. Rozwiązanie tych zadań nie jest wymagane. Stanowią wyłącznie pomoc w przygotowaniu się do zajęć.
Zadanie #1:
W kolejnych wierszach pliku znajdują się wyrażenia arytmetyczne zapisane w odwrotnej notacji polskiej (ale bez wykorzystania nawiasów). Napisz program, który będzie odczytywał wiersz i obliczał wartość, a jeżeli w wyrażeniu jest błąd składni, to rzucał wyjątek. Wyjątek zostaje przechwycony na końcu programu i obsłużony w postaci wyświetlenia komunikatu o rodzaju błędu. Dostosuj wartość wyjątku do rodzaju błędu.
Zadanie #2:
Napisz grę, która będzie losowała dowolne litery z alfabetu i wyświetlał je na ekranie, a zadaniem gracza jest podać szybko odpowiadający jej kod ascii. Fragment programu odpowiedzialny za zadawanie pytań i sprawdzanie odpowiedzi użytkownika jest zamknięty w funkcji, która zwraca liczbę całkowitą – sumaryczną liczbę sekund, jakiej użytkownik potrzebował, żeby udzielać odpowiedzi na serię pytań. Może zdarzyć się, że użytkownik poda niewłaściwy kod ascii, ale to nie powoduje przerwania gry. Dopiero w przypadku popełnienia przez użytkownika drugiej pomyłki zamiast kolejnego pytania rzucany jest wyjątek, który jest przechwycony dopiero w funkcji main
. W ramach obsługi wyjątku użytkownik dostaje pytanie, czy chce spróbować jeszcze raz i jeżeli tak – funkcja zadająca pytania jest ponownie wywoływana.
Zadanie #3:
Język „Markdown” jest jednym z najprostszych języków znaczników, mimo to jednak znajduje wiele zastosowań, m.in. w plikach readme. W tym języku można formatować tekst, który później zostanie odpowiednio zwizualizowany w oknie przeglądarki.
W języku „Markdown” wskazywanie czcionek, które mają być użyte w poszczególnych fragmentach dokumentu, odbywa się wg następującej składni: najpierw znak otwierający formatowanie, np. _
, *
, __
, **
lub `
, potem tekst podlegający formatowaniu, a na koniec ten sam znak, ale tym razem oznaczający koniec formatowanego fragmentu tekstu. Przykłady: _italic_
, *italic*
, __bold__
, **bold**
, `monospace`
.
Napisz parser, który będzie sprawdzał poprawność formatowania czcionek w tekście zawartym w pliku. Przyjmij, że poprawne formatowanie wymaga, aby liczba znaków otwierających i zamykających była sobie równa, oraz aby formatowane obszary nie przecinały się ani nie zagnieżdżały. W przypadku wykrycia błędu składni rzucany jest wyjątek, który zostanie przechwycony na końcu programu i obsłużony w postaci wyświetlenia komunikatu o błędzie. Dostosuj wartość wyjątku do rodzaju błędu.