Zaawansowane CPP/Ćwiczenia 11: Funktory: Różnice pomiędzy wersjami
Nie podano opisu zmian |
Nie podano opisu zmian |
||
Linia 4: | Linia 4: | ||
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 {mod10/exercises/compose_f_gx_hy.h}<tt>composefgxhy.h</tt>. | |||
</div></div> | |||
{{cwiczenie|2|| | {{cwiczenie|2|| | ||
Linia 67: | Linia 71: | ||
</div></div> | </div></div> | ||
''Uwaga: przekonwertowane latex2mediawiki; prawdopodobnie trzeba wprowadzi� poprawki'' | |||
{Funktory} | |||
'''Rozwiązanie 1 ''' | |||
'''Rozwiązanie 2 ''' | |||
W klasie {binder1st} 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; | |||
const bind_type _val; | |||
F _op; | |||
public: | |||
binder1st(F op,bind_type val):_op(op), _val(val) {}; | |||
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 {bind1st_f_type} 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; | |||
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; | |||
}; | |||
template<typename A1,typename R> | |||
struct bind1st_f_type<std::unary_function<A1,R> > { | |||
typedef generator<R> f_type; | |||
}; | |||
Całość klodu znajduje się w pliku {mod10/exercises/bind.h}<tt>bind.h</tt>. | |||
'''Rozwiązanie 3 ''' | |||
Zobacz plik {mod10/exercises/macro.h}<tt>macro.h</tt>. | |||
'''Rozwiązanie 4 ''' | |||
Zaczynamy od implemenatcji adaptera {call}. W tym celu defiuniujemy | |||
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; | |||
F _f; | |||
< | |||
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. {call_t1} 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 {mod10/exercises/call.h}<tt>call.h</tt>. | |||
Implementując adapter {macro} musimy umieć poznać która z dwu przekazanych | |||
funkcji ma wiecej argumentów. Używamy w tym celu szablonu {If_then_else}: | |||
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 {macro_type} i {call} 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 { macro_type<F1,F2>::m_type} | |||
defiuniujemy poprawne typy argumentów fuktora, ale niekoniecznie dobry | |||
typ wartości zwracanej. Dlatego redefinujemy go potem na {void}. | |||
Całość kodu znajduje się w pliku | |||
{mod10/exercises/mixed_macro.h}<tt>mixedmacro.h</tt>. |
Wersja z 10:40, 25 wrz 2006
Ćwiczenie 1
Zaimplementuj adapter compose_f_gx_hy
realizujący złożenie dwuargumentowe .
Ć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)
Uwaga: przekonwertowane latex2mediawiki; prawdopodobnie trzeba wprowadzi� poprawki
{Funktory}
Rozwiązanie 1
Rozwiązanie 2
W klasie {binder1st} 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; const bind_type _val; F _op; public: binder1st(F op,bind_type val):_op(op), _val(val) {};
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 {bind1st_f_type} 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;
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;
};
template<typename A1,typename R> struct bind1st_f_type<std::unary_function<A1,R> > {
typedef generator<R> f_type;
};
Całość klodu znajduje się w pliku {mod10/exercises/bind.h}bind.h.
Rozwiązanie 3
Zobacz plik {mod10/exercises/macro.h}macro.h.
Rozwiązanie 4
Zaczynamy od implemenatcji adaptera {call}. W tym celu defiuniujemy 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;
F _f; 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. {call_t1} 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 {mod10/exercises/call.h}call.h.
Implementując adapter {macro} musimy umieć poznać która z dwu przekazanych funkcji ma wiecej argumentów. Używamy w tym celu szablonu {If_then_else}:
template<typename F1,typename F2> struct macro_type {
typedef typename If_then_else< (size_t)functor_traits<F1>::n_args > = (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 {macro_type} i {call} 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 { macro_type<F1,F2>::m_type} defiuniujemy poprawne typy argumentów fuktora, ale niekoniecznie dobry typ wartości zwracanej. Dlatego redefinujemy go potem na {void}. Całość kodu znajduje się w pliku {mod10/exercises/mixed_macro.h}mixedmacro.h.