1
\$\begingroup\$

This is a follow-up question for A recursive_count_if Function For Various Type Arbitrary Nested Iterable Implementation in C++. Thanks to Quuxplusone's answer and G. Sliepen's comments. Based on the mentioned std::vector<std::vector<std::string>> case, it indicates a problem about the process of unwrapping nested iterable structure. In order to make recursive_count_if function be more generic, I am trying to implement another version of recursive_count_if template function with checking container's value_type matches specified type. The process of unwrapping nested iterable structure would keep running until the base type in container is as same as the given type T1.

//  recursive_count_if implementation
template<class T1, class T2, class T3> requires (is_iterable<T2> && std::same_as<std::iter_value_t<T2>, T1>)
auto recursive_count_if(const T2& input, const T3 predicate)
{
    return std::count_if(input.begin(), input.end(), predicate);
}

//  transform_reduce version
template<class T1, class T2, class T3> requires (is_iterable<T2> && !std::same_as<std::iter_value_t<T2>, T1>)
auto recursive_count_if(const T2& input, const T3 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<T1>(element, predicate);
        });
}

The test cases for this version recursive_count_if template function:

//  std::vector<std::vector<int>> case
std::vector<int> test_vector{ 1, 2, 3, 4, 4, 3, 7, 8, 9, 10 };
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);

// use a lambda expression to count elements divisible by 3.
int num_items1 = recursive_count_if<int>(test_vector2, [](int i) {return i % 3 == 0; });
std::cout << "#number divisible by three: " << num_items1 << '\n';

//  std::deque<std::deque<int>> case
std::deque<int> test_deque;
test_deque.push_back(1);
test_deque.push_back(2);
test_deque.push_back(3);

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);

// use a lambda expression to count elements divisible by 3.
int num_items2 = recursive_count_if<int>(test_deque2, [](int i) {return i % 3 == 0; });
std::cout << "#number divisible by three: " << num_items2 << '\n';

//  std::vector<std::vector<std::string>> case
std::vector<std::vector<std::string>> v = { {"hello"}, {"world"} };
auto size5 = [](std::string s) { return s.size() == 5; };
auto n = recursive_count_if<std::string>(v, size5);
std::cout << "n:" << n << std::endl;

The output of std::vector<std::vector<std::string>> case is:

n:2

Besides the std::vector<std::vector<std::string>> case, you can also play this recursive_count_if template function like this: [test_vector2 is from the above usage]

//  std::vector<std::vector<std::vector<int>>> case
std::vector<decltype(test_vector2)> test_vector3;
test_vector3.push_back(test_vector2);
test_vector3.push_back(test_vector2);
test_vector3.push_back(test_vector2);
std::cout << recursive_count_if<decltype(test_vector2)>(test_vector3,
    [test_vector2](auto& element)
    {
        return std::equal(element.begin(), element.end(), test_vector2.begin());
    }) << std::endl;

A Godbolt link is here.

All suggestions are welcome.

The summary information:

  • Which question it is a follow-up to?

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

  • What changes has been made in the code since last question?

    The previous version recursive_count_if template function is hard to deal with std::vector<std::vector<std::string>> case. The experimental improved code has been proposed here.

  • Why a new review is being asked for?

    In order to perform container's value_type matching structure, the std::same_as syntax and std::iter_value_t syntax are used here. The getting container's value_type part is handled by std::iter_value_t and the type matching part is handled by std::same_as. I am not sure if this is a good implementation.

    On the other hand, although the separated template parameter T1 plays the role of termination condition well, I am trying to deduce the type T1 form the given SFINAE-friendly lambda function (const T3 predicate here, assume that the given lambda function is SFINAE-friendly without something including auto syntax) so that making the following usage possible (just like the same usage in Quuxplusone's answer ).

    //  std::vector<std::vector<std::string>> case
    std::vector<std::vector<std::string>> v = { {"hello"}, {"world"} };
    auto size5 = [](std::string s) { return s.size() == 5; };
    auto n = recursive_count_if(v, size5);            //  the `<std::string>` is no needed to pass in again.
    std::cout << "n:" << n << std::endl;
    

    I've checked some discussions about getting the type of a lambda argument, including Can we get the type of a lambda argument?, Is it possible to retrieve the argument types from a (Functor member's) function signature for use in a template?, Getting the type of lambda arguments and C++ template deduction from lambda. I think that I have no idea about how to deduce the type T1 automatically. Is it possible done with std::function like the experimental code as below?

    template<class T1, class T2, class T3> requires (is_iterable<T1> && std::same_as<std::iter_value_t<T1>, T3>)
    auto recursive_count_if(const T1& input, const std::function<T2(T3)> predicate)
    {
      //...
    }
    

    However, I think that maybe T3 is not the input type of the passed lambda function in the above structure based on some experiments. If I misunderstand something, please tell me. Moreover, please give me some hints or examples if there is any other better way.

    If there is any further possible improvement, please let me know.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.