Zaawansowane CPP/Ćwiczenia 11: Funktory: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Mirek (dyskusja | edycje)
Nie podano opisu zmian
Mirek (dyskusja | edycje)
Nie podano opisu zmian
Linia 50: Linia 50:
  g(3.14);h(3.14,0)
  g(3.14);h(3.14,0)
}}
}}
'''Wskazówka '''  Można zacząć od zaimplementowania pomocniczego adaptera
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Podpowiedź</span><div class="mw-collapsible-content" style="display:none">
{call} który wywołuje funkcje dopasowując liczbę argumentów:
Można zacząć od zaimplementowania pomocniczego adaptera
<tt>call/tt>, który wywołuje funkcje dopasowując liczbę argumentów:
   
   
int f();
int f();
void g(double);
void g(double);
void h(double,int);
void h(double,int);<br>
call<double>(f)(x); /* woła f();*/
call<double>(g)(x); /* woła g();*/
call<double,double>(f)(x,y); /* woła f();*/
call<double,int>(g)(x,i); /* woła g(x);*/
call<double,int>(h)(x,i); /* woła h(x,i);*/


call<double>(f)(x); /* woła f();*/
Argumenty szablonu określają typy argumentów funktora zwracanego przez
call<double>(g)(x); /* woła g();*/
<tt>call<>()</tt>.
call<double,double>(f)(x,y); /* woła f();*/
</div></div>
call<double,int>(g)(x,i); /* woła g(x);*/
 
call<double,int>(h)(x,i); /* woła h(x,i);*/
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span><div class="mw-collapsible-content" style="display:none">
Nierekurencyjna funkcja przechodząca drzewo wgłąb może wyglądać następująco:
 
std::deque<node *> nodes;
nodes.push_back(_root);<br>
while(!nodes.empty()) {
  node *nd <nowiki> =</nowiki>    nodes.back();
  nodes.pop_back();<br>
  if(node->right) nodes.push_back(node->right);
  if(node->left)  nodes.push_back(node->left);
}
 
Iterator będzie wykonywał ten algorytm krok po kroku. W każdym kroku
węzeł <tt>nd</tt> będzie wezłem wskazywanym przez ten iterator.
Jako oznaczenie końca iteracji wykorzystamy iterator wskazujący na węzeł pusty.
 
Definujemy klasę zagnieżdżoną wewnątrz <tt>binary_tree</tt>:
 
class iterator {
std::deque<node *> _nodes;
node *_current;<br>
iterator(node *ptr <nowiki> =</nowiki>    0):_current(ptr) {
  if(_current) {
    if(_current->right)
        _nodes.push_back(_current->right);
    if(_current->left)
        _nodes.push_back(_current->left);
}<br>
iterator &operator++() {
  _current<nowiki> =</nowiki>  _nodes.back();
  _nodes.pop_back();<br>
  if(_current) {
    if(_current->right)
        _nodes.push_back(_current->right);
    if(_current->left)  
        _nodes.push_back(_current->left);
    }
    else
    _current<nowiki> =</nowiki>  0;
}
 
Żeby ta klasa zachowywała się jak wskaźnik dodajemy operatory:
 
public:
    T &operator*() {return _ptr->_val;};
    T *operator->() {return &(_ptr->_val);};
 
Potrzebny jest też operator porównania:
  bool operator<nowiki> =</nowiki>  <nowiki> =</nowiki>  (const trivial_iterator&lhs) const {
    return this->_ptr<nowiki> =</nowiki>  <nowiki> =</nowiki>  lhs._ptr;
  }
  bool operator!<nowiki> =</nowiki>  (const trivial_iterator&lhs) const {
    return !operator<nowiki> =</nowiki>  <nowiki> =</nowiki>  (lhs);
  }
 
Potrzebna jest jeszcze deklaracja:
   
   
Argumenty szablomnu określają typy argumentów funktora zwracanego przez
friend class binary_tree;   
{call<>()}.
 
żeby klasa <tt>binary_tree</tt> mogła korzystać z konstruktora
<tt>iterator(node *ptr)</tt>.
 
W klasie <tt>binary_tree</tt> definiujemy funkcje:
 
  iterator begin() {return iterator(_root);}
  iterator end(){return iterator(0);}
</div></div>

Wersja z 10:36, 25 wrz 2006

Ćwiczenie 1

Zaimplementuj adapter compose_f_gx_hy realizujący złożenie dwuargumentowe f(g(x),h(y)).

Ćwiczenie 2

Korzystając z klasy functor_traits zaimplementuj adpter bind1st, który bedzie działał zarówno dla funktorów jedno-, jak i dwuargumentowych.

Ćwiczenie 3

Zaimplementuj funktor implementujący, składanie funkcji poprzez wykonywanie ich po kolei np.:

macro(f1,f2)(x)

powinno wykonać

f1(x);f2(x);

Wartości zwracane przez te funkcje są ignorowane. Funkcja macro powinna zwracać funktor odpowiedniego typu (posiadający odpowiednie typy stowarzyszone) tak, aby możliwe było dalsze składanie np.:

macro(macro(f1,f2),f3)(x)

powinno wywołać:

f1(x);f2(x);f3(x);

Ćwiczenie 4

Zmodyfikuj powyższy szablon tak aby można było mieszać funkcje o różnej liczbie agrgumentów np.:

int f();
void g(double);
void h(double,int);
macro(f,g)(x);

powinno wywołać

f();g(x)}

a

macro(g,h)(3.14,0);

powinno wywołać

g(3.14);h(3.14,0)
Podpowiedź
Rozwiązanie