This is a follow-up question for A recursive_transform_reduce Function for Various Type Arbitrary Nested Iterable Implementation in C++, A recursive_count Function For Various Type Arbitrary Nested Iterable Implementation in C++, A recursive_count_if Function For Various Type Arbitrary Nested Iterable Implementation in C++, A Summation Function For Boost.MultiArray in C++, An arithmetic_mean Function For Various Type Arbitrary Nested Iterable Implementation in C++, A recursive_transform Template Function with Execution Policy and A population_variance Function For Various Type Arbitrary Nested Iterable Implementation in C++. Thanks to G. Sliepen's answer. I am trying to perform the suggestion that avoiding the requires clause if possible by using the concept name instead of class in the template parameter list
. As the result, the improved version of recursive_count
function, recursive_count_if
function, recursive_size
function, recursive_reduce
function, arithmetic_mean
function, recursive_transform
function, recursive_transform_reduce
function and population_variance
function are as below.
template<typename T>
concept is_back_inserterable = requires(T x)
{
std::back_inserter(x);
};
template<typename T>
concept is_elements_iterable = requires(T x)
{
std::begin(x)->begin();
std::end(x)->end();
};
template<typename T1, typename T2>
concept is_std_powable = requires(T1 x1, T2 x2)
{
std::pow(x1, x2);
};
template<typename T>
concept is_summable = requires(T x) { x + x; };
// recursive_count implementation
template<std::ranges::range T1, class T2> requires (!is_elements_iterable<T1>)
constexpr auto recursive_count(const T1& input, const T2 target)
{
return std::count(input.begin(), input.end(), target);
}
// transform_reduce version
template<is_elements_iterable T1, class T2>
constexpr auto recursive_count(const T1& input, const T2 target)
{
return std::transform_reduce(std::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [target](auto& element) {
return recursive_count(element, target);
});
}
// recursive_count_if implementation
template<std::ranges::range T1, class T2> requires (!is_elements_iterable<T1>)
constexpr auto recursive_count_if(const T1& input, const T2 predicate)
{
return std::count_if(input.begin(), input.end(), predicate);
}
// transform_reduce version
template<is_elements_iterable T1, class T2>
constexpr auto recursive_count_if(const T1& input, const T2 predicate)
{
return std::transform_reduce(std::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto& element) {
return recursive_count_if(element, predicate);
});
}
// recursive_size implementation
template<class T> requires (!std::ranges::range<T>)
constexpr auto recursive_size(const T& input)
{
return 1;
}
template<std::ranges::range T> requires (!is_elements_iterable<T>)
constexpr auto recursive_size(const T& input)
{
return input.size();
}
template<is_elements_iterable T>
constexpr auto recursive_size(const T& input)
{
return std::transform_reduce(std::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [](auto& element) {
return recursive_size(element);
});
}
// recursive_reduce implementation
template<class T, class ValueType, class Function = std::plus<ValueType>>
constexpr auto recursive_reduce(const T& input, ValueType init, const Function& f)
{
return f(init, input);
}
template<std::ranges::range Container, class ValueType, class Function = std::plus<ValueType>>
constexpr auto recursive_reduce(const Container& input, ValueType init, const Function& f = std::plus<ValueType>())
{
for (const auto& element : input) {
auto result = recursive_reduce(element, ValueType{}, f);
init = f(init, result);
}
return init;
}
template<typename T>
concept is_recursive_reduceable = requires(T x)
{
recursive_reduce(x, T{});
};
template<typename T>
concept is_recursive_sizeable = requires(T x)
{
recursive_size(x);
};
// arithmetic_mean implementation
template<class T = double, is_recursive_sizeable Container>
constexpr auto arithmetic_mean(const Container& input)
{
if (recursive_size(input) == 0) // Check the case of dividing by zero exception
{
throw std::logic_error("Divide by zero exception"); // Handle the case of dividing by zero exception
}
return (recursive_reduce(input, T{})) / (recursive_size(input));
}
// recursive_transform implementation
template<class T, class F>
constexpr auto recursive_transform(const T& input, const F& f)
{
return f(input);
}
template<class T, std::size_t S, class F>
constexpr auto recursive_transform(const std::array<T, S>& input, const F& f)
{
using TransformedValueType = decltype(recursive_transform(*input.cbegin(), f));
std::array<TransformedValueType, S> output;
std::transform(input.cbegin(), input.cend(), output.begin(),
[f](auto& element)
{
return recursive_transform(element, f);
}
);
return output;
}
template<template<class...> class Container, class Function, class... Ts>
requires (is_back_inserterable<Container<Ts...>> && std::ranges::range<Container<Ts...>> && !is_elements_iterable<Container<Ts...>>)
// non-recursive version
constexpr auto recursive_transform(const Container<Ts...>& input, const Function& f)
{
using TransformedValueType = decltype(f(*input.cbegin()));
Container<TransformedValueType> output;
std::transform(input.cbegin(), input.cend(), std::back_inserter(output), f);
return output;
}
template<template<class...> class Container, class Function, class... Ts>
requires (is_back_inserterable<Container<Ts...>> && is_elements_iterable<Container<Ts...>>)
constexpr auto recursive_transform(const Container<Ts...>& input, const Function& f)
{
using TransformedValueType = decltype(recursive_transform(*input.cbegin(), f));
Container<TransformedValueType> output;
std::transform(input.cbegin(), input.cend(), std::back_inserter(output),
[&](auto& element)
{
return recursive_transform(element, f);
}
);
return output;
}
#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
template<is_multi_array T, class F>
constexpr auto recursive_transform(const T& input, const F& f)
{
boost::multi_array output(input);
for (decltype(+input.shape()[0]) i = 0; i < input.shape()[0]; i++)
{
output[i] = recursive_transform(input[i], f);
}
return output;
}
#endif
// With execution policy
template<class ExPo, class T, class F>
constexpr auto recursive_transform(ExPo execution_policy, const T& input, const F& f)
{
return f(input);
}
template<class ExPo, class T, std::size_t S, class F>
requires std::is_execution_policy_v<std::remove_cvref_t<ExPo>>
constexpr auto recursive_transform(ExPo execution_policy, const std::array<T, S>& input, const F& f)
{
using TransformedValueType = decltype(recursive_transform(*input.cbegin(), f));
std::array<TransformedValueType, S> output;
std::transform(execution_policy, input.cbegin(), input.cend(), output.begin(),
[execution_policy, f](auto& element)
{
return recursive_transform(execution_policy, element, f);
}
);
return output;
}
template<class ExPo, template<class...> class Container, class Function, class... Ts>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> && std::ranges::range<Container<Ts...>> && !is_elements_iterable<Container<Ts...>>)
// non-recursive version
constexpr auto recursive_transform(ExPo execution_policy, const Container<Ts...>& input, const Function& f)
{
using TransformedValueType = decltype(f(*input.cbegin()));
Container<TransformedValueType> output(input.size());
std::transform(execution_policy, input.cbegin(), input.cend(), output.begin(), f);
return output;
}
template<class ExPo, template<class...> class Container, class Function, class... Ts>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>> && is_elements_iterable<Container<Ts...>>)
constexpr auto recursive_transform(ExPo execution_policy, const Container<Ts...>& input, const Function& f)
{
using TransformedValueType = decltype(recursive_transform(*input.cbegin(), f));
Container<TransformedValueType> output(input.size());
std::transform(execution_policy, input.cbegin(), input.cend(), output.begin(),
[&](auto& element)
{
return recursive_transform(execution_policy, element, f);
}
);
return output;
}
#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
template<class ExPo, is_multi_array T, class F>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_transform(ExPo execution_policy, const T& input, const F& f)
{
boost::multi_array output(input);
for (decltype(+input.shape()[0]) i = 0; i < input.shape()[0]; i++)
{
output[i] = recursive_transform(execution_policy, input[i], f);
}
return output;
}
#endif
// recursive_transform_reduce implementation
template<class Input, class T, class UnaryOp, class BinaryOp = std::plus<T>>
constexpr auto recursive_transform_reduce(const Input& input, T init, const UnaryOp& unary_op, const BinaryOp& binop = std::plus<T>())
{
return binop(init, unary_op(input));
}
template<std::ranges::range Input, class T, class UnaryOp, class BinaryOp = std::plus<T>>
constexpr auto recursive_transform_reduce(const Input& input, T init, const UnaryOp& unary_op, const BinaryOp& binop = std::plus<T>())
{
return std::transform_reduce(std::begin(input), std::end(input), init, binop, [&](auto& element) {
return recursive_transform_reduce(element, T{}, unary_op, binop);
});
}
template<typename T>
concept can_calculate_variance_of = requires(const T & value)
{
(std::pow(value, 2) - value) / std::size_t{ 1 };
};
template<typename T>
struct recursive_iter_value_t_detail
{
using type = T;
};
template <std::ranges::range T>
struct recursive_iter_value_t_detail<T>
: recursive_iter_value_t_detail<std::iter_value_t<T>>
{ };
template<typename T>
using recursive_iter_value_t = typename recursive_iter_value_t_detail<T>::type;
// population_variance function implementation (with recursive_transform_reduce template function)
template<class T = double, is_recursive_sizeable Container>
requires (can_calculate_variance_of<recursive_iter_value_t<Container>>)
constexpr auto population_variance(const Container& input)
{
if (recursive_size(input) == 0) // Check the case of dividing by zero exception
{
throw std::logic_error("Divide by zero exception"); // Handle the case of dividing by zero exception
}
auto mean = arithmetic_mean<T>(input);
return recursive_transform_reduce(std::execution::par,
input, T{}, [mean](auto& element) {
return std::pow(element - mean, 2);
}, std::plus<T>()) / recursive_size(input);
}
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
A recursive_count Function For Various Type Arbitrary Nested Iterable Implementation in C++,
A recursive_count_if Function For Various Type Arbitrary Nested Iterable Implementation in C++,
A Summation Function For Boost.MultiArray in C++,
An arithmetic_mean Function For Various Type Arbitrary Nested Iterable Implementation in C++,
A recursive_transform Template Function with Execution Policy and
A population_variance Function For Various Type Arbitrary Nested Iterable Implementation in C++
What changes has been made in the code since last question?
Avoiding requires clause if possible
Considering the warning
potential divide by 0
,if-throw
statement is added inarithmetic_mean
function andpopulation_variance
function.
Why a new review is being asked for?
After using the concept name instead of
class
in the template parameter list, I am not sure if concept name is still clear. Is it a good idea to remove the prefixis_
in the usage as above? If there is any possible improvement, please let me know.