Skip to content

Instantly share code, notes, and snippets.

@ubnt-intrepid
Last active August 29, 2015 14:15
Show Gist options
  • Save ubnt-intrepid/8e944f614d52186d4652 to your computer and use it in GitHub Desktop.
Save ubnt-intrepid/8e944f614d52186d4652 to your computer and use it in GitHub Desktop.
C++でFunctor
#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