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

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
m (Zastępowanie tekstu – „\displaystyle ” na „”)
 
(Nie pokazano 23 wersji utworzonych przez 3 użytkowników)
Linia 1: Linia 1:
'''Zadanie 1 '''  Zaimplementuj adapter <code><nowiki>compose_f_gx_hy</nowiki></code>
{{cwiczenie|1||
realizujący złożenie dwuargumentowe <math>\displaystyle f(g(x),h(y))</math>.


'''Zadanie 2 '''  Korzystając z klasy <code><nowiki>functor_traits</nowiki></code>
Zaimplementuj adapter <code><nowiki>compose_f_gx_hy</nowiki></code>
zaimplementuj adpter <code><nowiki>bind1st</nowiki></code> który bedzie działał zarówno dla
realizujący złożenie dwuargumentowe <math>f(g(x),h(y))</math>.
jedno i dwuargumentowych funktorów.
}}


'''Zadanie 3 '''  Rozszerz powyższy adapter tak, aby
<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">
działa  również dla jedno lub dwuargumentowych funkcji bez konieczności ich opakowywania.
Zobacz plik [[media:Compose_f_gx_hy.h | compose_f_gx_hy.h]].
</div></div>


'''Zadanie 4 '''
{{cwiczenie|2||


Zaimplementuj funktor impelemetujący, składanie funkcji poprzez
Korzystając z klasy <code><nowiki>functor_traits</nowiki></code>
wykonywanie ich po kolei:
zaimplementuj adpter <code><nowiki>bind1st</nowiki></code>, który bedzie działał zarówno dla funktorów jedno-, jak i dwuargumentowych.
}}


  <nowiki> macro1(f1,f2)(x)</nowiki>
<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">
W klasie <tt>binder1st</tt> umieszczamy dwa operatory nawiasów:
  template<typename F> class binder1st :
public  bind1st_f_type<typename functor_traits<F>::f_type>::f_type {
  typedef  typename functor_traits<F>::arg1_type  bind_type;<br> 
  const bind_type _val;
  F _op;
  public:
  binder1st(F op,bind_type val):_op(op), _val(val) {};<br>
  typename F::result_type operator()(typename functor_traits<F>::arg2_type x) {
    return _op(_val,x);
  }
  typename F::result_type operator()() {
    return _op(_val);
  }
};


wykonuje <code><nowiki>f1(x);f2(x);</nowiki></code>. Wartości zwracane przez te funkcje są
Dzięki konkretyzacji na żądanie wygenerowany zostanie tylko ten, którego użyjemy w kodzie. Szablon <tt>bind1st_f_type</tt> służy do określenia typu funktora po związaniu pierwszego argumentu. Do jego implemenatcji wykorzystujemy specjalizacje częściowe:
ignorowane. Podobnie dla funkcji <code><nowiki>macro2(f1,f2)</nowiki></code>, która ma zwrócić
funktor dwuargumentowy.
template<typename F> struct bind1st_f_type;<br>
template<typename A1,typename A2,typename R>  
  struct bind1st_f_type<std::binary_function<A1,A2,R> > {
  typedef std::unary_function<A2,R> f_type;
};<br>
template<typename A1,typename R>
struct bind1st_f_type<std::unary_function<A1,R> > {
  typedef generator<R> f_type;
};


'''Zadanie 5 ''' 
Całość kodu znajduje się w pliku [[media:Bind.h | bind.h]].
</div></div>


Zmodyfikuj powyższy szablon tak aby mogły być do niego przekazywane również funkcje bezargumentowe:
{{cwiczenie|3||


  <nowiki> int f();
Zaimplementuj funktor implementujący, składanie funkcji poprzez
void g(double);
wykonywanie ich po kolei np.:
   
macro(f1,f2)(x)


macro1(f,g)(x);</nowiki>
powinno wykonać
f1(x);f2(x);


spowdoduje wywołanie, <code><nowiki>f();g(x)</nowiki></code>.
Wartości zwracane przez te funkcje są ignorowane. Funkcja <tt>macro</tt> 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);
}}
 
<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">
Zobacz plik [[media:Macro.h | macro.h]].
</div></div>
 
{{cwiczenie|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);<br>
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)
}}
<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">
Można zacząć od zaimplementowania pomocniczego adaptera
<tt>call</tt>, który wywołuje funkcje dopasowując liczbę argumentów:
int f();
void g(double);
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);*/
 
Argumenty szablonu określają typy argumentów funktora zwracanego przez
<tt>call<>()</tt>.
</div></div>
 
<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">
Zaczynamy od implemenatcji adaptera <tt>call</tt>. W tym celu definiujemy
szablon:
 
template<typename F,typename A1,typename A2> struct call_t2: public
std::binary_function<A1,
                      A2,
                      typename functor_traits<F>::result_type> {
  typedef typename functor_traits<F>::result_type result_type;
  typedef typename functor_traits<F>::arg1_type arg1_type;
  typedef typename functor_traits<F>::arg2_type arg2_type;<br>
  F _f;<br>
public:
call_t2(F f):_f(f) {};
 
który wyposażamy w trzy funkcje:
  result_type call(A1 a1,A2 a2,
                  generator<result_type>) {
    return _f();
  };
  result_type call(A1 a1,A2 a2,
                  std::unary_function<arg1_type,result_type>) {
    return _f(a1);
  };
  result_type call(A1 a1,A2 a2,
                  std::binary_function<arg1_type,arg2_type,result_type>) {
    return _f(a1,a2);
  };
 
Ostatni argument służy do tylko do przeciążenia funkcji wykorzystanego w operatorze nawiasów:
  result_type operator()(A1 a1,A2 a2) {
    return call(a1,a2,typename functor_traits<F>::f_type());
  };
 
Jak zwykle dodajemy funkcję:
template<typename A1,typename A2,typename F> call_t2<F,A1,A2> call(F f) {
return call_t2<F,A1,A2>(f);}
 
Podobnie definiujemy jednoargumentową wersję tego szablonu <tt>call_t1</tt> i przeciążoną funkcję:
 
template<typename A1,typename F> call_t1<F,A1> call(F f) {
return call_t1<F,A1>(f);}
 
Całość kodu znajduje się w pliku [[media:Call.h | call.h]].
 
Implementując adapter <tt>macro</tt> musimy umieć poznać która z dwu przekazanych funkcji ma wiecej argumentów. Używamy w tym celu szablonu <tt>If_then_else</tt>:
 
template<typename F1,typename F2> struct macro_type {
  typedef typename If_then_else<
    (size_t)functor_traits<F1>::n_args
      ><nowiki> =</nowiki>      
    (size_t)functor_traits<F2>::n_args ,
    typename functor_traits<F1>::f_type,
    typename functor_traits<F2>::f_type>::Result  m_type;
};
 
Korzystając z <tt>macro_type</tt> i <tt>call</tt> możemy zaimplementować szablon:
 
template<typename F1,typename F2>  class macro_t :
public macro_type<F1,F2>::m_type {
  public:
  typedef void result_type ;
 
Proszę zwrócić uwagę, że dziedzicząc z <tt>macro_type<F1,F2>::m_type</tt>
defiuniujemy poprawne typy argumentów fuktora, ale niekoniecznie dobry
typ wartości zwracanej. Dlatego redefinujemy go potem na <tt>void</tt>.
Całość kodu znajduje się w pliku [[media:Mixed_macro.h | mixed_macro.h]].
</div></div>

Aktualna wersja na dzień 09:00, 28 sie 2023

Ćwiczenie 1

Zaimplementuj adapter compose_f_gx_hy realizujący złożenie dwuargumentowe .

Rozwiązanie

Ć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.

Rozwiązanie

Ć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);
Rozwiązanie

Ć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