This is a follow-up question for A recursive_count_if Function For Various Type Arbitrary Nested Iterable Implementation in C++, A recursive_count_if Function with Specified value_type for Various Type Arbitrary Nested Iterable Implementation in C++ and A recursive_count_if Function with Automatic Type Deducing from Lambda for Various Type Arbitrary Nested Iterable Implementation in C++. As G. Sliepen's answer Don't try to deduce the predicate's parameter type
and Quuxplusone's answer mentioned I think the user of this API should have to tell the function explicitly how many "levels" to unwrap downward
, I am trying to implement another version recursive_count_if
template function with unwrap level parameter. Thanks to Quuxplusone for providing the hint of using if constexpr
to achieve this.
template<std::size_t unwrap_level, class T1, class T2> requires (is_iterable<T1>)
auto recursive_count_if(const T1& input, const T2 predicate)
{
if constexpr (unwrap_level > 1)
{
return std::transform_reduce(std::cbegin(input), std::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto& element) {
return recursive_count_if<unwrap_level - 1>(element, predicate);
});
}
else
{
return std::count_if(input.begin(), input.end(), predicate);
}
}
The used is_iterable
concept is as below. I keep it in order to get nicer error messages if user accidentily try to apply this recursive_count_if()
on something that can not be iterated over.
template<typename T>
concept is_iterable = requires(T x)
{
*std::begin(x);
std::end(x);
};
Some test cases are as below. With this version recursive_count_if
, you can always use auto
keyword in the input lambda parameter.
// 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<2>(test_vector2, [](auto& 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<2>(test_deque2, [](auto& i) {return i % 3 == 0; });
std::cout << "#number divisible by three: " << num_items2 << '\n';
std::vector<std::vector<std::string>> v = { {"hello"}, {"world"} };
auto size5 = [](auto& s) { return s.size() == 5; };
auto n = recursive_count_if<2>(v, size5);
std::cout << "n:" << n << std::endl;
// 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<1>(test_vector3,
[test_vector2](auto& element)
{
return std::equal(element.begin(), element.end(), test_vector2.begin());
}) << std::endl;
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?
This version of
recursive_count_if
template function is with unwrap level parameter, you can always useauto
keyword in the input lambda parameter.Why a new review is being asked for?
With the given unwrap level parameter, the behavior of
recursive_count_if
template function may be more clear and be easy to use. If there is any further possible improvement, please let me know.