From 13983f9a6ec9782ede283105d78454aa20acbd46 Mon Sep 17 00:00:00 2001 From: Alexis Pereda Date: Wed, 14 Aug 2019 16:40:58 +0200 Subject: [PATCH] [et] lambda/curry --- examples/et/5_currying.cpp | 221 ++++++++++++++++++++++++++++++++ examples/et/5_lambda.cpp | 252 +++++++++++++++++++++++++++++++++++++ 2 files changed, 473 insertions(+) create mode 100644 examples/et/5_currying.cpp create mode 100644 examples/et/5_lambda.cpp diff --git a/examples/et/5_currying.cpp b/examples/et/5_currying.cpp new file mode 100644 index 0000000..8682f10 --- /dev/null +++ b/examples/et/5_currying.cpp @@ -0,0 +1,221 @@ +#include +#include + +/* **** */ +/* tags */ + +namespace tag { + +struct Argument; + +struct Add; +struct Mul; +struct Minus; + +} + +/* ********** */ +/* expression */ + +template +struct Expr { + std::tuple operands; + + Expr(Operands... operands): operands{operands...} {} +}; + +template +struct Argument {}; + +namespace arg { + +Argument<0> _0; +Argument<1> _1; +Argument<2> _2; +Argument<3> _3; +// ... + +} + +/* *********** */ +/* type traits */ + +namespace impl { +template struct IsExpr: std::false_type {}; + +template +struct IsExpr>: std::true_type {}; + +template +struct IsExpr>: std::true_type {}; +} + +template +constexpr bool IsExpr = impl::IsExpr::value; + +template< + typename LHS, typename RHS, + std::enable_if_t && IsExpr>* = nullptr +> +auto operator+(LHS lhs, RHS rhs) { + return Expr{lhs, rhs}; +} + +template< + typename LHS, typename RHS, + std::enable_if_t && IsExpr>* = nullptr +> +auto operator*(LHS lhs, RHS rhs) { + return Expr{lhs, rhs}; +} + +template< + typename RHS, + std::enable_if_t>* = nullptr +> +auto operator-(RHS rhs) { + return Expr{rhs}; +} + +/* ********** */ +/* evaluators */ + +template struct EvaluatorElem; + +template<> +struct EvaluatorElem { + template + static auto eval(T value) { return value; } +}; + +template<> +struct EvaluatorElem { + template + static auto eval(T lhs, T rhs) { return lhs + rhs; } +}; + +template<> +struct EvaluatorElem { + template + static auto eval(T lhs, T rhs) { return lhs * rhs; } +}; + +template<> +struct EvaluatorElem { + template + static auto eval(T rhs) { return -rhs; } +}; + +/* *** */ + +namespace impl { + +template struct GetExprOp; +template +struct GetExprOp> { + using type = Op; +}; + +} + +template +using GetExprOp = typename impl::GetExprOp::type; + +namespace impl { + +template struct MaxArgument; +template +struct MaxArgument: + std::integral_constant::value> {}; + +template +struct MaxArgument, Arguments...> { + static constexpr std::size_t M = MaxArgument::value; + static constexpr std::size_t value = I>M? I:M; +}; + +template +struct MaxArgument, Arguments...> { + static constexpr std::size_t M0 = MaxArgument::value; + static constexpr std::size_t M1 = MaxArgument::value; + static constexpr std::size_t value = M0>M1? M0:M1; +}; + +template<> struct MaxArgument<>: std::integral_constant {}; + +} + +template +constexpr std::size_t MaxArgument = impl::MaxArgument::value; + +template class EvaluatorTemplate, typename BoundArgs = std::tuple<>, bool Impl = false> +struct Evaluable { + using Evaluator = EvaluatorTemplate>; + + Expression expr; + BoundArgs boundArgs; + + static constexpr std::size_t arity = MaxArgument + 1 - std::tuple_size{}; + + template= arity>* = nullptr> + auto operator()(Arguments... arguments) { + static_assert(Impl || sizeof...(Arguments) <= arity, "too many arguments"); + + using IndexSequence = std::make_index_sequence{}>; + return evalBound(IndexSequence{}, arguments...); + } + + template* = nullptr> + auto operator()(Arguments... arguments) { + auto newBoundArgs = std::tuple_cat(boundArgs, std::make_tuple(arguments...)); + return Evaluable{expr, newBoundArgs}; + } + +private: + template + auto evalBound(std::index_sequence, Arguments... arguments) { + using IndexSequence = std::make_index_sequence{}>; + return eval(IndexSequence{}, std::get(boundArgs)..., arguments...); + } + + template + auto eval(std::index_sequence, Arguments... arguments) { + using IndexSequence = std::index_sequence; + return Evaluator::eval(std::get(resolveOperands(IndexSequence{}, expr.operands, arguments...))...); + } + + template + auto resolveOperands(std::index_sequence, T tuple, Arguments... arguments) { + return std::make_tuple(resolveOperand(std::get(tuple), arguments...)...); + } + + template + auto resolveOperand(Argument, Arguments... arguments) { + return std::get(std::make_tuple(arguments...)); + } + + template + auto resolveOperand(Expr expr, Arguments... arguments) { + return Evaluable, EvaluatorTemplate, std::tuple<>, true>{expr, {}}(arguments...); + } +}; + +template class Evaluator, typename Expr> +auto bind(Expr expr) { + return Evaluable{expr, {}}; +} + +int main() { + int volatile v0 = 3, v1 = 5, v2 = 7, v3 = 9; + int result; + + using namespace arg; + + auto expr = _1 * -(_2 + _0) + _3 * _0; + auto fElem = bind(expr); + + auto fCurry = fElem(v0); + result = fCurry(v1)(v2)(v3); + + std::printf("result: %d\n", result); +} diff --git a/examples/et/5_lambda.cpp b/examples/et/5_lambda.cpp new file mode 100644 index 0000000..cd08a91 --- /dev/null +++ b/examples/et/5_lambda.cpp @@ -0,0 +1,252 @@ +#include +#include +#include +#include + +/* **** */ +/* tags */ + +namespace tag { + +struct Argument; + +struct Add; +struct Mul; +struct Minus; +struct Less; + +} + +/* ********** */ +/* expression */ + +template +struct Expr { + std::tuple operands; + + Expr(Operands... operands): operands{operands...} {} +}; + +template +struct Argument {}; + +namespace arg { + +Argument<0> _0; +Argument<1> _1; +Argument<2> _2; +Argument<3> _3; +// ... + +} + +/* *********** */ +/* type traits */ + +namespace impl { +template struct IsExpr: std::false_type {}; + +template +struct IsExpr>: std::true_type {}; + +template +struct IsExpr>: std::true_type {}; +} + +template +constexpr bool IsExpr = impl::IsExpr::value; + +template< + typename LHS, typename RHS, + std::enable_if_t && IsExpr>* = nullptr +> +auto operator+(LHS lhs, RHS rhs) { + return Expr{lhs, rhs}; +} + +template< + typename LHS, typename RHS, + std::enable_if_t && IsExpr>* = nullptr +> +auto operator*(LHS lhs, RHS rhs) { + return Expr{lhs, rhs}; +} + +template< + typename RHS, + std::enable_if_t>* = nullptr +> +auto operator-(RHS rhs) { + return Expr{rhs}; +} + +template< + typename LHS, typename RHS, + std::enable_if_t && IsExpr>* = nullptr +> +auto operator<(LHS lhs, RHS rhs) { + return Expr{lhs, rhs}; +} + +/* ********** */ +/* evaluators */ + +template struct EvaluatorElem; + +template<> +struct EvaluatorElem { + template + static auto eval(T value) { return value; } +}; + +template<> +struct EvaluatorElem { + template + static auto eval(T lhs, T rhs) { return lhs + rhs; } +}; + +template<> +struct EvaluatorElem { + template + static auto eval(T lhs, T rhs) { return lhs * rhs; } +}; + +template<> +struct EvaluatorElem { + template + static auto eval(T rhs) { return -rhs; } +}; + +template<> +struct EvaluatorElem { + template + static auto eval(T lhs, T rhs) { return lhs < rhs; } +}; + +/* *** */ + +namespace impl { + +template struct GetExprOp; +template +struct GetExprOp> { + using type = Op; +}; + +} + +template +using GetExprOp = typename impl::GetExprOp::type; + +namespace impl { + +template struct MaxArgument; +template +struct MaxArgument: + std::integral_constant::value> {}; + +template +struct MaxArgument, Arguments...> { + static constexpr std::size_t M = MaxArgument::value; + static constexpr std::size_t value = I>M? I:M; +}; + +template +struct MaxArgument, Arguments...> { + static constexpr std::size_t M0 = MaxArgument::value; + static constexpr std::size_t M1 = MaxArgument::value; + static constexpr std::size_t value = M0>M1? M0:M1; +}; + +template<> struct MaxArgument<>: std::integral_constant {}; + +} + +template +constexpr std::size_t MaxArgument = impl::MaxArgument::value; + +template class EvaluatorTemplate, bool Impl = false> +struct Evaluable { + using Evaluator = EvaluatorTemplate>; + + Expression expr; + + static constexpr std::size_t arity = MaxArgument + 1; + + template= arity)>* = nullptr> + auto operator()(Arguments... arguments) { + static_assert(Impl || sizeof...(Arguments) <= arity, "too many arguments"); + + using IndexSequence = std::make_index_sequence{}>; + return eval(IndexSequence{}, arguments...); + } + + template* = nullptr> + auto operator()(Arguments...) { + static_assert(sizeof...(Arguments) >= arity, "too few arguments"); + } + +private: + template + auto eval(std::index_sequence, Arguments... arguments) { + using IndexSequence = std::index_sequence; + return Evaluator::eval(std::get(resolveOperands(IndexSequence{}, expr.operands, arguments...))...); + } + + template + auto resolveOperands(std::index_sequence, T tuple, Arguments... arguments) { + return std::make_tuple(resolveOperand(std::get(tuple), arguments...)...); + } + + template + auto resolveOperand(Argument, Arguments... arguments) { + return std::get(std::make_tuple(arguments...)); + } + + template + auto resolveOperand(Expr expr, Arguments... arguments) { + return Evaluable, EvaluatorTemplate, true>{expr}(arguments...); + } +}; + +template class Evaluator, typename Expr> +auto bind(Expr expr) { + return Evaluable{expr}; +} + +/* ******** */ +/* examples */ +void directCall() { + using namespace arg; + + int volatile v0 = 3, v1 = 5, v2 = 7, v3 = 9; + int result; + + auto expr = _1 * -(_2 + _0) + _3 * _0; + auto fElem = bind(expr); + + result = fElem(v0, v1, v2, v3); + + std::printf("result: %d\n", result); +} + +void lambda() { + using namespace arg; + + std::vector v{5, 7, 3, 1, 6, 9, 4, 2}; + std::vector w(v.size()); + + std::sort(std::begin(v), std::end(v), bind(_1 < _0)); + std::transform(std::begin(v), std::begin(w), std::begin(w), bind(_0 * _0)); + + for(auto i: v) std::printf("%d ", i); + std::puts(""); + + for(auto i: w) std::printf("%d ", i); + std::puts(""); +} + +int main() { + directCall(); + lambda(); +}