Last active
August 29, 2015 14:15
-
-
Save ubnt-intrepid/8e944f614d52186d4652 to your computer and use it in GitHub Desktop.
C++でFunctor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <algorithm> | |
#include <functional> | |
#include <type_traits> | |
#include <vector> | |
#include <iostream> | |
#include <iterator> | |
#include <boost/optional.hpp> | |
using boost::optional; | |
using boost::none; | |
// std::vectorはテンプレート引数が2つ必要なためNG | |
template <typename T> | |
using vector = std::vector<T, std::allocator<T>>; | |
namespace detail { | |
template < | |
template <class> class Functor, | |
typename T | |
> | |
struct functor_impl { | |
template <class F> | |
static auto fmap(F f, Functor<T> arg) | |
-> Functor<typename std::result_of<F(T)>::type>; | |
}; | |
// vector<T>の部分特殊化 | |
template <typename T> | |
struct functor_impl<vector, T> | |
{ | |
template <class F> | |
static auto fmap(F f, vector<T> arg) | |
-> vector<typename std::result_of<F(T)>::type> | |
{ | |
vector<typename std::result_of<F(T)>::type> ret; | |
if (!arg.empty()) { | |
std::transform(std::begin(arg), std::end(arg), | |
std::back_inserter(ret), std::forward<F>(f)); | |
} | |
return ret; | |
} | |
}; | |
// optional<T>の部分特殊化 | |
template <typename T> | |
struct functor_impl<optional, T> | |
{ | |
template <class F> | |
static auto fmap(F f, optional<T> arg) | |
-> optional<typename std::result_of<F(T)>::type> | |
{ | |
if (arg) return f(*arg); | |
else return none; | |
} | |
}; | |
} // namespace detail; | |
template < | |
template <class> class Functor, | |
typename F, typename Arg | |
> | |
inline auto fmap(F f, Functor<Arg> arg) | |
-> Functor<typename std::result_of<F(Arg)>::type> | |
{ | |
return detail::functor_impl<Functor, Arg>::fmap(f, arg); | |
} | |
template < | |
template <class> class Functor, | |
typename Arg, typename F | |
> | |
inline auto fmap(F f) | |
-> std::function<Functor<typename std::result_of<F(Arg)>::type>(Functor<Arg>)> | |
{ | |
return [=](Functor<Arg> arg) -> Functor<typename std::result_of<F(Arg)>::type> { | |
return detail::functor_impl<Functor, Arg>::fmap(f, arg); | |
}; | |
} | |
struct func_t { | |
template <typename T> | |
double operator()(T a) { return a * 3.0; } | |
} func; | |
int main() | |
{ | |
{ | |
// fmap :: (a -> b) -> (f a -> f b) | |
auto const fin = [](int a){ return a*2.0; }; | |
// fout : optional<int> -> optional<double> | |
auto fout = fmap<optional, int>(fin); | |
// 変換後の関数を実行する | |
// 元の関数にはない`optionalの値に応じた処理`が自動的に適用される | |
optional<double> out1 = fout(optional<int>(10)); // Just 10 | |
optional<double> out2 = fout(none); // Nothing | |
} | |
{ | |
// fout : optional<int> -> optional<double> | |
auto fout = fmap<vector, int>([](int v){ return v*5.0; }); | |
vector<double> out1 = fout(vector<int>{1,2,3}); // [5, 10, 15] | |
vector<double> out2 = fout(vector<int>()); // [] | |
} | |
{ | |
vector<int> in{ 1, 2, 3 }; | |
std::cout << "In: "; | |
std::copy(std::begin(in), std::end(in), std::ostream_iterator<int>(std::cout, ",")); | |
std::cout << "\n"; | |
// auto out = fmap<vector>([](int a){ return a * 2.0; }, in); | |
vector<double> out = fmap<vector>(func, in); | |
std::cout << "Out: "; | |
std::copy(std::begin(out), std::end(out), std::ostream_iterator<int>(std::cout, ",")); | |
std::cout << "\n"; | |
} | |
{ | |
using namespace std; | |
optional<int> in = 10; | |
cout << "In: " << *in << endl; | |
optional<double> out = fmap<optional>([](int a){ return a*2.0; }, in); | |
cout << "Out: " << *out << endl; | |
in = none; | |
out = fmap<optional>([](int a){ return a*2.0; }, in); | |
std::cout << std::boolalpha << static_cast<bool>(out) << std::endl; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment