Zaawansowane CPP/Ćwiczenia 11: Funktory: Różnice pomiędzy wersjami
Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania(Nie pokazano 23 wersji utworzonych przez 2 użytkowników) | |||
Linia 1: | Linia 1: | ||
− | + | {{cwiczenie|1|| | |
+ | |||
+ | Zaimplementuj adapter <code><nowiki>compose_f_gx_hy</nowiki></code> | ||
realizujący złożenie dwuargumentowe <math>\displaystyle f(g(x),h(y))</math>. | realizujący złożenie dwuargumentowe <math>\displaystyle f(g(x),h(y))</math>. | ||
+ | }} | ||
+ | |||
+ | <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:Compose_f_gx_hy.h | compose_f_gx_hy.h]]. | ||
+ | </div></div> | ||
+ | |||
+ | {{cwiczenie|2|| | ||
+ | |||
+ | Korzystając z klasy <code><nowiki>functor_traits</nowiki></code> | ||
+ | zaimplementuj adpter <code><nowiki>bind1st</nowiki></code>, który bedzie działał zarówno dla funktorów jedno-, jak i dwuargumentowych. | ||
+ | }} | ||
+ | |||
+ | <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); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | 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: | ||
+ | |||
+ | 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; | ||
+ | }; | ||
+ | |||
+ | Całość kodu znajduje się w pliku [[media:Bind.h | bind.h]]. | ||
+ | </div></div> | ||
+ | |||
+ | {{cwiczenie|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 <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ń 10:47, 2 paź 2006
Ć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