A recursive_count Function For Various Type Arbitrary Nested Iterable Implementation in C++

Posted on

Problem

This is a follow-up question for A Summation Function For Arbitrary Nested Vector Implementation In C++ and A Summation Function For Various Type Arbitrary Nested Iterable Implementation in C++. Besides the summation result, I am trying to get the element count in arbitrary nested iterable things. For example, there are three elements in std::vector<int> test_vector = { 1, 2, 3 };, so the element count of test_vector is 3. The experimental implementation of recursive_count is as below.

size_t recursive_count()
{
    return 0;
}

template<class T> requires (!is_elements_iterable<T> && !is_iterable<T>)
size_t recursive_count(const T& input)
{
    return 1;
}

template<class T> requires (!is_elements_iterable<T> && is_iterable<T>)
size_t recursive_count(const T& input)
{
    return input.size();
}

template<class T> requires (is_elements_iterable<T> && is_iterable<T>)
size_t recursive_count(const T& input)
{
    size_t output{};
    for (auto &element : input)
    {
        output += recursive_count(element);
    }
    return output;
}

Some test cases of this recursive_count template function.

//  std::vector<int> case
std::vector<int> test_vector = {
    1, 2, 3
};
auto recursive_count_result1 = recursive_count(test_vector);
std::cout << recursive_count_result1 << std::endl;

//  std::vector<std::vector<int>> case
std::vector<decltype(test_vector)> test_vector2 = {
    test_vector, test_vector, test_vector
};
auto recursive_count_result2 = recursive_count(test_vector2);
std::cout << recursive_count_result2 << std::endl;

// std::deque<int> case
std::deque<int> test_deque;
test_deque.push_back(1);
test_deque.push_back(1);
test_deque.push_back(1);
auto recursive_count_result3 = recursive_count(test_deque);
std::cout << recursive_count_result3 << std::endl;

// std::deque<std::deque<int>> case
std::deque<decltype(test_deque)> test_deque2;
test_deque2.push_back(test_deque);
test_deque2.push_back(test_deque);
test_deque2.push_back(test_deque);
auto recursive_count_result4 = recursive_count(test_deque2);
std::cout << recursive_count_result4 << std::endl;

A Godbolt link is here.

All suggestions are welcome.

The summary information:

Solution

No need for an overload that takes no parameters

Nothing calls recursive_count() without parameters, so that overload is unnecessary.

Unnecessary requirements

You don’t need to require both is_elements_iterable<T> and is_iterable<T>, the former will already cover the requirements of the latter, and if you negate them then the latter covers the former. So the overloads should be:

template<class T> requires !is_iterable<T>
size_t recursive_count(const T& input)
{
    return 1;
}

template<class T> requires (!is_elements_iterable<T> && is_iterable<T>)
size_t recursive_count(const T& input)
{
    return input.size();
}

template<class T> requires is_elements_iterable<T>
size_t recursive_count(const T& input)
{
    size_t output{};
    for (auto &element : input)
    {
        output += recursive_count(element);
    }
    return output;
}

Naming

Indeed you already noticed that STL has a std::count that does something different. However there is a function that does what you want for non-nested containers: std::size. So consider naming your function std::recursive_size.

Consider making more use of STL algorithms

You are implementing your own algorithms but you are not making use of STL’s algorithms. This can reduce the amount of code you write. For example, the last overload can be rewritten as:

template<class T> requires is_elements_iterable<T>
size_t recursive_count(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_count(element);
    });
}

Although I’m not sure this is more readable than your version.

Leave a Reply

Your email address will not be published. Required fields are marked *