This is a follow-up question for A Summation Function For Various Type Arbitrary Nested Iterable Implementation in C++, An arithmetic_mean Function For Various Type Arbitrary Nested Iterable Implementation in C++, A non-nested test_vectors_generator Function for arithmetic_mean Function Testing in C++ and Non-nested std::deque and std::list Generator Function for arithmetic_mean Function Testing in C++. Besides calculating summation and arithmetic mean value of arbitrary nested iterable, I am trying to implement an arithmetic_variance
function which can calculate population variance value with the following formula.
is population variance value of x,
is the value of i-th element,
is the population mean which is calculated by
arithmetic_mean
template function and N is the population size which is calculated by recursive_size
function (refer to the previous question A recursive_count Function For Various Type Arbitrary Nested Iterable Implementation in C++).
The usage description
The input of population_variance
template function is a arbitrary Nested Iterable. For example, given a test_vector: std::vector<double> test_vector{ 1, 2, 3, 4, 5 };
. The population_variance
template function can be called like this std::cout << "population_variance: " << population_variance(test_vector) << std::endl;
and the output is
population_variance: 2
The experimental implementation
The experimental implementation of population_variance
template function is here.
// population_variance function implementation
template<class T1, class T2>
requires (is_iterable<T1> && is_recursive_reduceable<T1> && is_recursive_sizeable<T1> && is_minusable2<std::iter_value_t<T1>, T2>)
// non-recursive version
auto _population_variance(const T1& input, const T2 arithmetic_mean_result)
{
return std::transform_reduce(std::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [arithmetic_mean_result](auto& element) {
return std::pow(element - arithmetic_mean_result, 2);
});
}
template<class T1, class T2>
requires (is_iterable<T1> && is_elements_iterable<T1> && is_recursive_reduceable<T1> && is_recursive_sizeable<T1>)
auto _population_variance(const T1& input, const T2 arithmetic_mean_result)
{
return std::transform_reduce(std::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [arithmetic_mean_result](auto& element) {
return _population_variance(element, arithmetic_mean_result);
});
}
template<class T>
requires (is_recursive_reduceable<T> && is_recursive_sizeable<T>)
auto population_variance(const T& input)
{
return _population_variance(input, arithmetic_mean(input)) / (recursive_size(input));
}
The used is_iterable
, is_elements_iterable
, is_recursive_reduceable
, is_recursive_sizeable
and is_minusable2
concepts are as below.
template<typename T>
concept is_iterable = requires(T x)
{
*std::begin(x);
std::end(x);
};
template<typename T>
concept is_elements_iterable = requires(T x)
{
std::begin(x)->begin();
std::end(x)->end();
};
template<typename T>
concept is_recursive_reduceable = requires(T x)
{
recursive_reduce(x, 0.0);
};
template<typename T>
concept is_recursive_sizeable = requires(T x)
{
recursive_size(x);
};
template<typename T>
concept is_minusable = requires(T x) { x - x; };
template<typename T1, typename T2>
concept is_minusable2 = requires(T1 x1, T2 x2) { x1 - x2; };
The implementation of the used arithmetic_mean
function:
template<class T> requires (is_recursive_reduceable<T> && is_recursive_sizeable<T>)
auto arithmetic_mean(const T& input)
{
return (recursive_reduce(input, 0.0)) / (recursive_size(input));
}
The implementation of the used recursive_size
function:
// recursive_size implementation
template<class T> requires (!is_iterable<T>)
auto recursive_size(const T& input)
{
return 1;
}
template<class T> requires (!is_elements_iterable<T> && is_iterable<T>)
auto recursive_size(const T& input)
{
return input.size();
}
template<class T> requires (is_elements_iterable<T>)
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);
});
}
Test cases
A more complex example is like:
// std::vector<std::vector<int>> case
std::vector<double> test_vector{ 1, 2, 3, 4, 5 };
std::cout << "recursive_size of test_vector: " << recursive_size(test_vector) << std::endl;
std::cout << "population_variance of test_vector: " << population_variance(test_vector) << std::endl;
std::vector<decltype(test_vector)> test_vector2;
test_vector2.push_back(test_vector);
test_vector2.push_back(test_vector);
test_vector2.push_back(test_vector);
std::cout << "recursive_size of test_vector2: " << recursive_size(test_vector2) << std::endl;
std::cout << "population_variance of test_vector2: " << population_variance(test_vector2) << std::endl;
auto test_vector3 = n_dim_container_generator<10, std::vector, decltype(test_vector)>(test_vector, 3);
std::cout << "recursive_size of test_vector3: " << recursive_size(test_vector3) << std::endl;
std::cout << "population_variance of test_vector3: " << population_variance(test_vector3) << std::endl;
The used n_dim_container_generator
template function is as follows. Thanks to G. Sliepen's answer.
template<std::size_t dim, template<class...> class Container = std::vector, class T>
constexpr auto n_dim_container_generator(T input, std::size_t times)
{
if constexpr (dim == 0)
{
return input;
}
else
{
return Container(times, n_dim_container_generator<dim - 1, Container, T>(input, times));
}
}
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
A Summation Function For Various Type Arbitrary Nested Iterable Implementation in C++,
An arithmetic_mean Function For Various Type Arbitrary Nested Iterable Implementation in C++,
A non-nested test_vectors_generator Function for arithmetic_mean Function Testing in C++ and
Non-nested std::deque and std::list Generator Function for arithmetic_mean Function Testing in C++
What changes has been made in the code since last question?
Besides the functions for calculating summation and arithmetic mean value of arbitrary nested iterable, a new function
population_variance
implementation is the main part of this question.Why a new review is being asked for?
There are more concepts used here, including
is_iterable
,is_elements_iterable
,is_recursive_reduceable
,is_recursive_sizeable
andis_minusable2
. Please check if the design is appropriate or not.