391 lines
14 KiB
C++
Executable File
391 lines
14 KiB
C++
Executable File
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
|
|
// https://github.com/Dobiasd/FunctionalPlus
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
#pragma once
|
|
|
|
#include <fplus/function_traits.hpp>
|
|
#include <fplus/internal/apply.hpp>
|
|
#include <fplus/internal/asserts/functions.hpp>
|
|
#include <fplus/internal/asserts/composition.hpp>
|
|
#include <fplus/internal/composition.hpp>
|
|
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
|
|
namespace fplus
|
|
{
|
|
|
|
// API search type: bind_1st_of_2 : (((a, b) -> c), a) -> (b -> c)
|
|
// Bind first parameter of binary function.
|
|
template <typename F, typename T>
|
|
auto bind_1st_of_2(F f, T x)
|
|
{
|
|
return [f, x](auto&& y) {
|
|
internal::trigger_static_asserts<internal::bind_1st_of_2_tag,
|
|
F,
|
|
T,
|
|
decltype(y)>();
|
|
return internal::invoke(f, x, std::forward<decltype(y)>(y));
|
|
};
|
|
}
|
|
|
|
// API search type: bind_2nd_of_2 : (((a, b) -> c), b) -> (a -> c)
|
|
// Bind second parameter of binary function.
|
|
template <typename F, typename T>
|
|
auto bind_2nd_of_2(F f, T y)
|
|
{
|
|
return [f, y](auto&& x) {
|
|
internal::trigger_static_asserts<internal::bind_2nd_of_2_tag,
|
|
F,
|
|
decltype(x),
|
|
T>();
|
|
return internal::invoke(f, std::forward<decltype(x)>(x), y);
|
|
};
|
|
}
|
|
|
|
// API search type: bind_1st_of_3 : (((a, b, c) -> d), a) -> ((b, c) -> d)
|
|
// Bind first parameter of ternary function.
|
|
template <typename F, typename X>
|
|
auto bind_1st_of_3(F f, X x)
|
|
{
|
|
return [f, x](auto&& y, auto&& z) {
|
|
internal::trigger_static_asserts<internal::bind_1st_of_3_tag,
|
|
F,
|
|
X,
|
|
decltype(y),
|
|
decltype(z)>();
|
|
return internal::invoke(
|
|
f, x, std::forward<decltype(y)>(y), std::forward<decltype(z)>(z));
|
|
};
|
|
}
|
|
|
|
// API search type: bind_1st_and_2nd_of_3 : (((a, b, c) -> d), a, b) -> (c -> d)
|
|
// Bind first and second parameter of ternary function.
|
|
template <typename F, typename X, typename Y>
|
|
auto bind_1st_and_2nd_of_3(F f, X x, Y y)
|
|
{
|
|
return [f, x, y](auto&& z) {
|
|
internal::trigger_static_asserts<internal::bind_1st_and_2nd_of_3_tag,
|
|
F,
|
|
X,
|
|
Y,
|
|
decltype(z)>();
|
|
return internal::invoke(f, x, y, std::forward<decltype(z)>(z));
|
|
};
|
|
}
|
|
|
|
// API search type: bind_2nd_and_3rd_of_3 : (((a, b, c) -> d), b, c) -> (a -> d)
|
|
// Bind first and second parameter of ternary function.
|
|
template <typename F, typename Y, typename Z>
|
|
auto bind_2nd_and_3rd_of_3(F f, Y y, Z z)
|
|
{
|
|
return [f, y, z](auto&& x) {
|
|
internal::trigger_static_asserts<internal::bind_2nd_and_3rd_of_3_tag,
|
|
F,
|
|
decltype(x),
|
|
Y,
|
|
Z>();
|
|
return internal::invoke(f, std::forward<decltype(x)>(x), y, z);
|
|
};
|
|
}
|
|
|
|
// API search type: flip : (a -> b) -> (b -> a)
|
|
// Flips the arguments of a binary function
|
|
// Note: The callable can take a variadic number of arguments
|
|
template <typename F>
|
|
auto flip(F f)
|
|
{
|
|
return [f](auto&&... args) {
|
|
return internal::apply_impl(
|
|
f,
|
|
std::forward_as_tuple(std::forward<decltype(args)>(args)...),
|
|
internal::make_reverse_index_sequence<sizeof...(args)>{});
|
|
};
|
|
}
|
|
|
|
// API search type: forward_apply : (a, (a -> b)) -> b
|
|
// Forward application.
|
|
// Returns the result of applying the function f to the value x.
|
|
template <typename X, typename F>
|
|
auto forward_apply(X&& x, F f)
|
|
{
|
|
internal::trigger_static_asserts<internal::unary_function_tag, F, X>();
|
|
return internal::invoke(f, std::forward<X>(x));
|
|
}
|
|
|
|
// API search type: lazy : ((a -> b), a) -> (() -> b)
|
|
// Lazy evaluation.
|
|
// Returns a function evaluating f with the given arguments when called.
|
|
// Also known as defer.
|
|
// Note: f can take a variadic number of parameters
|
|
template<typename F, typename... Args>
|
|
auto lazy(F f, Args ... args)
|
|
{
|
|
return [f, args...] {
|
|
internal::trigger_static_asserts<internal::check_arity_tag, F, Args...>();
|
|
return internal::invoke(f, args...);
|
|
};
|
|
}
|
|
|
|
// API search type: fixed : a -> (() -> a)
|
|
// Identity as a nullary function.
|
|
// Returns a function returning x when called.
|
|
// Like lazy with identity as f.
|
|
template<typename T>
|
|
auto fixed(T x)
|
|
{
|
|
return [x]() -> T
|
|
{
|
|
return x;
|
|
};
|
|
}
|
|
|
|
// API search type: compose : ((a -> b), (b -> c)) -> (a -> c)
|
|
// Forward function composition.
|
|
// compose(f, g)(x) = g(f(x))
|
|
// It is possible to compose a variadic number of callables.
|
|
// The first callable can also take a variadic number of parameters.
|
|
// compose(f, g, h)(x, y, z) = h(g(f(x, y, z)))
|
|
template <typename... Fs>
|
|
auto compose(Fs&&... fs)
|
|
{
|
|
return internal::compose_impl<Fs...>(std::forward<Fs>(fs)...);
|
|
}
|
|
|
|
// API search type: logical_not : (a -> Bool) -> (a -> Bool)
|
|
// Converts a predicate p into a new one,
|
|
// always returning the exact opposite of p.
|
|
// logical_not(f) = \x -> !x
|
|
// Note: F can take a variadic number of parameters.
|
|
// Equivalent to std::not_fn (C++17)
|
|
template <typename Predicate>
|
|
auto logical_not(Predicate f)
|
|
{
|
|
return [f](auto&&... args) {
|
|
internal::trigger_static_asserts<internal::unary_function_tag,
|
|
Predicate,
|
|
decltype(args)...>();
|
|
using Res =
|
|
std::decay_t<internal::invoke_result_t<Predicate, decltype(args)...>>;
|
|
static_assert(std::is_same<Res, bool>::value, "Function must return bool.");
|
|
|
|
return !internal::invoke(f, std::forward<decltype(args)>(args)...);
|
|
};
|
|
}
|
|
|
|
// API search type: logical_or : ((a -> Bool), (a -> Bool)) -> (a -> Bool)
|
|
// logical_or(f, g) = \x -> f(x) or g(x)
|
|
// Combines to unary predicates into a single one
|
|
// that holds true if at least one of the original predicated is true.
|
|
template <typename UnaryPredicateF, typename UnaryPredicateG>
|
|
auto logical_or(UnaryPredicateF f, UnaryPredicateG g)
|
|
{
|
|
auto op = [](auto f1, auto f2, auto x) {
|
|
return internal::invoke(f1, x) || internal::invoke(f2, x);
|
|
};
|
|
|
|
return internal::logical_binary_op(op, f, g);
|
|
}
|
|
|
|
// API search type: logical_and : ((a -> Bool), (a -> Bool)) -> (a -> Bool)
|
|
// logical_and(f, g) = \x -> f(x) and g(x)
|
|
// Combines to unary predicates into a single one
|
|
// that holds true if both original predicated are true.
|
|
template <typename UnaryPredicateF, typename UnaryPredicateG>
|
|
auto logical_and(UnaryPredicateF f, UnaryPredicateG g)
|
|
{
|
|
auto op = [](auto f1, auto f2, auto x) {
|
|
return internal::invoke(f1, x) && internal::invoke(f2, x);
|
|
};
|
|
|
|
return internal::logical_binary_op(op, f, g);
|
|
}
|
|
|
|
// API search type: logical_xor : ((a -> Bool), (a -> Bool)) -> (a -> Bool)
|
|
// logical_xor(f, g) = \x -> f(x) xor g(x)
|
|
// Combines to unary predicates into a single one
|
|
// that holds true if exactly one of the original predicated is true.
|
|
template <typename UnaryPredicateF, typename UnaryPredicateG>
|
|
auto logical_xor(UnaryPredicateF f, UnaryPredicateG g)
|
|
{
|
|
auto op = [](auto f1, auto f2, auto x) {
|
|
return internal::invoke(f1, x) != internal::invoke(f2, x);
|
|
};
|
|
|
|
return internal::logical_binary_op(op, f, g);
|
|
}
|
|
|
|
// API search type: memoize : (a -> b) -> (a -> b)
|
|
// Provides Memoization for a given (referentially transparent)
|
|
// unary function.
|
|
// Returns a closure mutating an internally held dictionary
|
|
// mapping input values to output values.
|
|
template <typename F,
|
|
typename FIn = typename utils::function_traits<F>::template arg<0>::type,
|
|
typename FOut = typename std::result_of<F(FIn)>::type,
|
|
typename MemoMap = std::unordered_map<
|
|
typename std::remove_reference<typename std::remove_const<FIn>::type>::type,
|
|
FOut>>
|
|
std::function<FOut(FIn)> memoize(F f)
|
|
{
|
|
static_assert(utils::function_traits<F>::arity == 1, "Wrong arity.");
|
|
MemoMap storage;
|
|
return [=](FIn x) mutable -> FOut
|
|
{
|
|
const auto it = storage.find(x);
|
|
if (it == storage.end())
|
|
{
|
|
return storage.emplace(x, internal::invoke(f, x)).first->second;
|
|
}
|
|
else
|
|
{
|
|
return it->second;
|
|
}
|
|
};
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
template <typename F, typename Cache,
|
|
typename FIn1 = typename utils::function_traits<F>::template arg<0>::type,
|
|
typename FIn2 = typename utils::function_traits<F>::template arg<1>::type,
|
|
typename FOut = typename std::result_of<F(FIn1, FIn2)>::type,
|
|
typename ResultF = std::function<FOut(FIn2)>>
|
|
ResultF memoize_recursive_helper(const F f, std::shared_ptr<Cache> storage)
|
|
{
|
|
return [f, storage](FIn2 x)
|
|
{
|
|
const auto it = storage->find(x);
|
|
if (it == storage->end())
|
|
{
|
|
const auto g = memoize_recursive_helper(f, storage);
|
|
(*storage)[x] = f(g, x);
|
|
}
|
|
return (*storage)[x];
|
|
};
|
|
}
|
|
} // namespace internal
|
|
|
|
// API search type: memoize_recursive : (a -> b) -> (a -> b)
|
|
// Provides Memoization for a given (referentially transparent)
|
|
// recursive binary function that takes a continuation as first argument.
|
|
// e.g.
|
|
// uint64_t fibo_cont(const std::function<uint64_t(uint64_t)>& cont, uint64_t n)
|
|
// {
|
|
// if (n < 2) return n;
|
|
// else return cont(n-1) + cont(n-2);
|
|
// }
|
|
// Returns a closure mutating an internally held dictionary
|
|
// mapping input values to output values.
|
|
template <typename F,
|
|
typename FIn1 = typename utils::function_traits<F>::template arg<0>::type,
|
|
typename FIn2 = typename utils::function_traits<F>::template arg<1>::type,
|
|
typename FOut = typename std::result_of<F(FIn1, FIn2)>::type,
|
|
typename MemoMap = std::unordered_map<
|
|
typename std::remove_reference<typename std::remove_const<FIn2>::type>::type,
|
|
FOut>>
|
|
std::function<FOut(FIn2)> memoize_recursive(F f)
|
|
{
|
|
std::shared_ptr<MemoMap> storage = std::make_shared<MemoMap>();
|
|
return internal::memoize_recursive_helper(f, storage);
|
|
}
|
|
|
|
// API search type: memoize_binary : ((a, b) -> c) -> ((a, b) -> c)
|
|
// Provides Memoization for a given (referentially transparent)
|
|
// binary function.
|
|
// Returns a closure mutating an internally held dictionary
|
|
// mapping input values to output values.
|
|
template <typename F,
|
|
typename FIn1 = typename utils::function_traits<F>::template arg<0>::type,
|
|
typename FIn2 = typename utils::function_traits<F>::template arg<1>::type,
|
|
typename FOut = typename std::result_of<F(FIn1, FIn2)>::type,
|
|
typename ParamPair = std::pair<
|
|
typename std::remove_reference<typename std::remove_const<FIn1>::type>::type,
|
|
typename std::remove_reference<typename std::remove_const<FIn2>::type>::type>,
|
|
typename MemoMap = std::unordered_map<ParamPair, FOut>>
|
|
std::function<FOut(FIn1, FIn2)> memoize_binary(F f)
|
|
{
|
|
const auto unary_f = [f](const ParamPair& params) -> FOut
|
|
{
|
|
return internal::invoke(f, params.first, params.second);
|
|
};
|
|
auto unary_f_memoized = memoize<decltype(unary_f),
|
|
ParamPair, FOut, std::map<ParamPair, FOut>>(unary_f);
|
|
return [unary_f_memoized](FIn1 a, FIn2 b) mutable -> FOut
|
|
{
|
|
return unary_f_memoized(std::make_pair(a, b));
|
|
};
|
|
}
|
|
|
|
// API search type: constructor_as_function : a -> b
|
|
// struct foo
|
|
// {
|
|
// foo(int a, int b) : a_(a), b_(2*b) {}
|
|
// int a_;
|
|
// int b_;
|
|
// };
|
|
// const auto create_foo = constructor_as_function<foo, int, int>;
|
|
// create_foo(1,2) == foo(1, 2);
|
|
template <typename T, class ... Types>
|
|
T constructor_as_function(Types ... args)
|
|
{
|
|
return T(args...);
|
|
}
|
|
|
|
} // namespace fplus
|
|
|
|
#define fplus_get_mem(fplus_get_mem_name) \
|
|
[](const auto& fplus_get_mem_x) \
|
|
{ \
|
|
return fplus_get_mem_x.fplus_get_mem_name; \
|
|
}
|
|
|
|
#define fplus_get_ptr_mem(fplus_get_ptr_mem_name) \
|
|
[](const auto& fplus_get_ptr_mem_x) \
|
|
{ \
|
|
return fplus_get_ptr_mem_x->fplus_get_ptr_mem_name; \
|
|
}
|
|
|
|
#define fplus_get_c_mem_t(fplus_get_c_mem_t_c, fplus_get_c_mem_t_name, fplus_get_c_mem_t_t) \
|
|
[](const fplus_get_c_mem_t_c& fplus_get_c_mem_t_x) -> fplus_get_c_mem_t_t \
|
|
{ \
|
|
return fplus_get_c_mem_t_x.fplus_get_c_mem_t_name; \
|
|
}
|
|
|
|
#define fplus_get_c_ptr_mem_t(fplus_get_c_ptr_mem_t_c, fplus_get_c_ptr_mem_t_name, fplus_get_c_ptr_mem_t_t) \
|
|
[](const fplus_get_c_ptr_mem_t_c& fplus_get_c_ptr_mem_t_x) -> fplus_get_c_ptr_mem_t_t \
|
|
{ \
|
|
return fplus_get_c_ptr_mem_t_x->fplus_get_c_ptr_mem_t_name; \
|
|
}
|
|
|
|
#define fplus_mem_fn(fplus_mem_fn_name) \
|
|
[](const auto& fplus_mem_fn_x) \
|
|
{ \
|
|
return fplus_mem_fn_x.fplus_mem_fn_name(); \
|
|
}
|
|
|
|
#define fplus_ptr_mem_fn(fplus_ptr_mem_fn_name) \
|
|
[](const auto& fplus_ptr_mem_fn_x) \
|
|
{ \
|
|
return fplus_ptr_mem_fn_x->fplus_ptr_mem_fn_name(); \
|
|
}
|
|
|
|
#define fplus_c_mem_fn_t(fplus_c_mem_fn_t_c, fplus_c_mem_fn_t_name, fplus_c_mem_fn_t_t) \
|
|
[](const fplus_c_mem_fn_t_c& fplus_c_mem_fn_t_x) -> fplus_c_mem_fn_t_t \
|
|
{ \
|
|
return fplus_c_mem_fn_t_x.fplus_c_mem_fn_t_name(); \
|
|
}
|
|
|
|
#define fplus_c_ptr_mem_fn_t(fplus_c_ptr_mem_fn_t_c, fplus_c_ptr_mem_fn_t_name, fplus_c_ptr_mem_fn_t_t) \
|
|
[](const fplus_c_ptr_mem_fn_t_c& fplus_c_ptr_mem_fn_t_x) -> fplus_c_ptr_mem_fn_t_t \
|
|
{ \
|
|
return fplus_c_ptr_mem_fn_t_x->fplus_c_ptr_mem_fn_t_name(); \
|
|
}
|