虽然输出结果完全相同,但我认为Arrow应该是自己的类,这样它的定义域和值域可以存储为类型。现在返回类型可以在不需要编译器推断的情况下得知。
#include <functional>
#include <iostream>
#include <string>
#include <sstream>
template <typename A, typename B>
class Arrow {
const std::function<B(const A&)> arrow;
public:
Arrow (const std::function<B(const A&)>& a) : arrow(a) {}
B operator()(const A& a) const {return arrow(a);}
using domain = A;
using range = B;
};
template <typename A, typename B, typename C>
Arrow<A,C> operator * (const Arrow<A,B>& arrow_ab, const Arrow<B,C>& arrow_bc) {
return Arrow<A,C>([arrow_ab, arrow_bc](const A& a)->C {return arrow_bc(arrow_ab(a));});
}
template <typename First, typename... Rest> struct LastType : LastType<Rest...> {};
template <typename T> struct Identity { using type = T; };
template <typename Last> struct LastType<Last> : Identity<Last> {};
template <typename...> struct ComposeArrows;
template <typename First, typename... Rest>
struct ComposeArrows<First, Rest...> : ComposeArrows<Rest...> {
using last_arrow = typename LastType<Rest...>::type;
using composed_arrow = Arrow<typename First::domain, typename last_arrow::range>;
composed_arrow operator()(const First& first, const Rest&... rest) {
return first * ComposeArrows<Rest...>::operator()(rest...);
}
};
template <typename Last>
struct ComposeArrows<Last> {
Last operator()(const Last& arrow) {return arrow;}
};
template <typename... Arrows>
typename ComposeArrows<Arrows...>::composed_arrow composeArrows (const Arrows&... arrows) {
return ComposeArrows<Arrows...>()(arrows...);
}
template <typename T>
std::string toString (const T& t) {
std::ostringstream os;
os << t;
return os.str();
}
int main() {
Arrow<int, double> plusHalf ([](int i){return i + 0.5;});
Arrow<double, std::string> doubleToString ([](float f){return toString(f) + " is the result";});
Arrow<std::string, char> lastCharacter ([](const std::string& str)->int {show(str) return str.back();});
const Arrow<int, char> composed = composeArrows (plusHalf, doubleToString, lastCharacter);
std::cout << composed(2) << std::endl;
std::cout << composeArrows (plusHalf, doubleToString, lastCharacter)(2) << std::endl;
}
以下是另一种语法略有不同的解决方案:
#include <iostream>
#include <functional>
#include <tuple>
#include <string>
template <typename A, typename B>
struct Arrow {
using domain = const A&;
using range = B;
using Function = std::function<range(domain)>;
const Function& function;
Arrow (const Function& f) : function(f) {}
range operator()(domain x) const {return function(x);}
};
template <typename... Ts>
struct LastType {
using Tuple = std::tuple<Ts...>;
using type = typename std::tuple_element<std::tuple_size<Tuple>::value - 1, Tuple>::type;
};
template <typename Arrow1, typename Arrow2>
typename Arrow2::range compose (const typename Arrow1::domain& x, const Arrow2& arrow2, const Arrow1& arrow1) {
return arrow2(arrow1(x));
}
template <typename Arrow, typename... Rest>
auto compose (const typename LastType<Rest...>::type::domain& x, const Arrow& arrow, const Rest&... rest) {
return arrow(compose(x, rest...));
}
int main() {
Arrow<std::string, int> f([](const std::string& s) {return s.length();});
Arrow<int, double> g([](int x) {return x + 0.5;});
Arrow<double, int> h([](double d) {return int(d+1);});
std::cout << compose("hello", g, f) << '\n';
std::cout << compose("hello", h, g, f) << '\n';
}
auto f = f1 * f2 * f3 * f4;
即可。或者您想更像函数调用的方式吗?(auto f = compose(f1, f2, f3, f4);
) - Guilherme Bernal