1. Pretpostavimo da se u programu za vektorsku grafiku javlja potreba za enkapsuliranjem oblika i crteža prema sljedećim sučeljima:
class Point{
public:
double x_;
double y_;
//...
};
class Shape{
public:
virtual const std::string& id() const =0;
virtual void translate(const Point& pt) =0;
// ...
};
class Drawing{
public:
virtual void addShape(Shape* pShape)=0;
virtual void removeShape(const std::string& id)=0;
virtual Shape& getShape(int i)=0;
virtual int nShapes()=0;
};
Implementiraj pravocrtni segment
(ShapeLineSegment)
kao najjednostavniju implementaciju
sučelja Shape.
Oblikuj implementaciju sučelja crteža (Drawing)
kao prilagodnik za linearni spremnik
iz standardne biblioteke (DrawingVector).
Neka vanjski razred u C++-u bude std::vector,
u Pythonu list, a u Javi ArrayList.
Napiši jednostavno ispitno okruženje
te ispitaj ispravan rad razvijenih komponenata.
2. I dalje smo kod programa za vektorsku grafiku. Implementiraj prolaz kroz elemente crteža u skladu s oblikovnim obrascem Iterator. Oblikuj iterator kao odgovarajući korisnički razred. Prepravi prilagodnik prema spremniku biblioteke u skladu s novim sučeljem:
getShape jer
ona implicira linearni uređaj elemenata crteža,
Sad ponudi alternativnu implementaciju crteža
temeljenu na binarnom stablu (DrawingMap)
koje za ključ koristi identifikacijski kod elemenata crteža
(njega vraća metoda Shape::id()).
Neka vanjski razred u C++-u bude std::map,
u Pythonu dict, a u Javi TreeMap.
Pokaži da klijenti mogu transparentno koristiti
i jednu i drugu implementaciju crteža
zahvaljujući polimorfnim iteratorima.
Ispitaj ispravan rad razvijenih komponenata.
3. Razvijamo program za upravljanje strojem za pripremanje toplih napitaka. U prvoj demonstracijskoj verziji potrebno je podržati barem dva različita napitka, te se odlučujemo za čaj i kavu.
Poznato je da se kava radi po sljedećem receptu:
- zakuhaj vodu - umiješaj kavu - izlij u posudu - dodaj šećer i mlijeko
Čaj se sprema na sličan način:
- zakuhaj vodu - umetni vrećicu čaja - izlij u posudu - dodaj limun
Oblikuj rješenje problema prema obrascu okvirne metode. Neka su elementi obrasca kako slijedi:
Neka svaka pozvana metoda na standardnom izlazu ispiše dijagnostički izlaz kako bi se mogao pratiti tijek izvođenja programa.
4.
Proučite način korištenja pametnih pokazivača biblioteke
boost.
Proučite implementaciju razreda scoped_ptr
u datoteci boost/smart_ptr/scoped_ptr.hpp.
Napišite kratke ispitne programe za
scoped_ptr i shared_ptr.
Napomena: ovaj zadatak mora se riješiti u C++-u.
Trebali biste steći razumijevanje
kako spomenute komponente rade,
barem na konceptualnoj razini.
5.
Potrebno je napisati jednostavni rekurzivni program
za parsiranje, prikazivanje i evaluiranje aritmetičkih izraza.
Program treba podržavati operacije
zbrajanja, oduzimanja, množenja i dijeljenja,
te grupiranje zagradama nad brojčanim i simboličkim podatcima.
Evaluiranje simboličkih podataka implementirajte
prozivanjem globalnog rječnika Symbols.
Neka se program temelji na funkciji parseExpression
koja treba kreirati kompozit koji predstavlja sintaksno stablo izraza.
Sve komponente kompozita trebaju definirati konstruktor,
metodu toStr koja sadržaj kompozita izražava znakovnim nizom
(u Pythonu ovu metodu ima smisla nazvati
__str__ ili __repr__),
te metodu evaluate koja evaluira vrijednost kompozita.
Ispitajte razvijeni program na način da ručno postavite vrijednosti simbola, pokrenete parsiranje izraza zadanog znakovim nizom, te zatim ispišete parsirani izraz kao i njegovu numeričku vrijednost. Preporučeni primjer ispitivanja u interaktivnoj ljusci Pythona prikazan je u nastavku.
>>> tree=parseExpression("6*(x+4)/2-3-x")
>>> tree.toStr()
((((6.0*(x+4.0))/2.0)-3.0)-x)
>>> tree.evaluate() # ovo ne radi jer ne znamo x!
...
KeyError: 'x'
>>> Symbols['x']=5
>>> tree.evaluate()
19.0
>>> x=5; 6*(x+4)/2-3-x # proba
19.0
>>> Symbols['x']=4 # radi i za drugi x
>>> tree.evaluate()
17.0
Pomoć
Parsiranje može biti vrlo težak zadatak ako problemu ne pristupimo na pravi način. Situaciju posebno kompliciraju sljedeći problemi:
Stoga elegantno rješenje možemo postići ako razrješavamo operatore redoslijedom koji odgovara njihovom prioritetu. Pri tome moramo paziti da zanemarimo operatore koji su uokvireni zagradama. Kako bismo pogodili asocijativnost, operatore treba uzimati u obzir s desna na lijevo. Kad obradimo sve operatore, potrebno je provjeriti radi li se o izrazu kojeg grupiraju zagrade. Ako to nije slučaj, vraćamo atomarni izraz koji može biti broj ili simbol.
Kôd koji implementira gore skicirani algoritam
parsiranja prikazan je u nastavku.
Prikazani kôd treba modificirati na način da
na izlazu proizvede kompozit
kojeg jednostavno možemo ispisati i evaluirati,
u skladu s uputama danim na početku zadatka.
Traženi program može se napisati
u manje od 60 redaka Pythona,
uključujući i 20 redaka modificirane funkcije parse.
# adapted from http://news.ycombinator.com/item?id=284842
def parse(strinput):
for operator in ["+-", "*/"]:
depth = 0
for p in range(len(strinput) - 1, -1, -1):
if strinput[p] == ')': depth += 1
elif strinput[p] == '(': depth -= 1
elif depth==0 and strinput[p] in operator:
# strinput is a compound expression
return (strinput[p], parse(strinput[:p]), parse(strinput[p+1:]))
strinput = strinput.strip()
if strinput[0] == '(':
# strinput is a parenthesized expression?
return parse(strinput[1:-1])
# strinput is an atom!
return strinput