2
\$\begingroup\$

This is a follow-up question for A recursive_transform for std::vector with various return type and A recursive_transform Template Function Implementation with std::invocable concept in C++. Besides the recursive version std::ranges::transform, I am trying to implement a recursive version std::ranges::copy_if.

The experimental implementation

The experimental implementation is as below.

//  recursive_copy_if function 
template <std::ranges::input_range Range, std::invocable<std::ranges::range_value_t<Range>> UnaryPredicate>
constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate)
{
    Range output{};
    std::ranges::copy_if(std::ranges::cbegin(input), std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        unary_predicate);
    return output;
}

template <
    std::ranges::input_range Range,
    class UnaryPredicate>
requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>)
constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate)
{
    Range output{};
    
    std::ranges::transform(
        std::ranges::cbegin(input),
        std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        [&unary_predicate](auto&& element) { return recursive_copy_if(element, unary_predicate); }
        );
    return output;
}

Test cases

//  std::vector<int>
std::vector<int> test_vector = {
    1, 2, 3, 4, 5, 6
};
recursive_print(recursive_copy_if(test_vector, [](int x) { return (x % 2) == 0; }));

//  std::vector<std::vector<int>>
std::vector<decltype(test_vector)> test_vector2 = {
    test_vector, test_vector, test_vector
};
recursive_print(recursive_copy_if(test_vector2, [](int x) { return (x % 2) == 0; }));

//  std::vector<std::string>
recursive_print(
    recursive_copy_if(
        recursive_transform(test_vector, [](int x) { return std::to_string(x); }),
        [](std::string x) { return (x == "1"); }
    )
);

//  std::vector<std::vector<std::string>>
recursive_print(
    recursive_copy_if(
        recursive_transform(test_vector2, [](int x) { return std::to_string(x); }),
        [](std::string x) { return (x == "1"); }
    )
);

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

recursive_print(recursive_copy_if(test_deque, [](int x) { return (x % 2) == 0; }));


//  std::deque<std::deque<int>>
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);

recursive_print(recursive_copy_if(test_deque2, [](int x) { return (x % 2) == 0; }));

//  std::list<int>
std::list<int> test_list = { 1, 2, 3, 4, 5, 6 };

recursive_print(recursive_copy_if(test_list, [](int x) { return (x % 2) == 0; }));


//  std::list<std::list<int>>
std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };

recursive_print(recursive_copy_if(test_list2, [](int x) { return (x % 2) == 0; }));

Full Testing Code

The full testing code:

#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <complex>
#include <concepts>
#include <deque>
#include <execution>
#include <exception>
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <mutex>
#include <numeric>
#include <optional>
#include <ranges>
#include <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>

//  recursive_copy_if function 
template <std::ranges::input_range Range, std::invocable<std::ranges::range_value_t<Range>> UnaryPredicate>
constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate)
{
    Range output{};
    std::ranges::copy_if(std::ranges::cbegin(input), std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        unary_predicate);
    return output;
}

template <
    std::ranges::input_range Range,
    class UnaryPredicate>
requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>)
constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate)
{
    Range output{};
    
    std::ranges::transform(
        std::ranges::cbegin(input),
        std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        [&unary_predicate](auto&& element) { return recursive_copy_if(element, unary_predicate); }
        );
    return output;
}

//  recursive_print implementation
//  https://codereview.stackexchange.com/q/251208/231235
template<std::ranges::input_range Range>
constexpr auto recursive_print(const Range& input, const int level = 0)
{
    auto output = input;
    std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl;
    std::transform(std::ranges::cbegin(input), std::ranges::cend(input), std::ranges::begin(output),
        [level](auto&& x)
        {
            std::cout << std::string(level, ' ') << x << std::endl;
            return x;
        }
    );
    return output;
}

template<std::ranges::input_range Range> requires (std::ranges::input_range<std::ranges::range_value_t<Range>>)
constexpr auto recursive_print(const Range& input, const int level = 0)
{
    auto output = input;
    std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl;
    std::transform(std::ranges::cbegin(input), std::ranges::cend(input), std::ranges::begin(output),
        [level](auto&& element)
        {
            return recursive_print(element, level + 1);
        }
    );
    return output;
}

//  recursive_invoke_result_t implementation
template<typename, typename>
struct recursive_invoke_result { };

template<typename T, std::invocable<T> F>
struct recursive_invoke_result<F, T> { using type = std::invoke_result_t<F, T>; };

template<typename F, template<typename...> typename Container, typename... Ts>
requires (
    !std::invocable<F, Container<Ts...>> &&
    std::ranges::input_range<Container<Ts...>> &&
    requires { typename recursive_invoke_result<F, std::ranges::range_value_t<Container<Ts...>>>::type; })
struct recursive_invoke_result<F, Container<Ts...>>
{
    using type = Container<typename recursive_invoke_result<F, std::ranges::range_value_t<Container<Ts...>>>::type>;
};

template<typename F, typename T>
using recursive_invoke_result_t = typename recursive_invoke_result<F, T>::type;

//  recursive_transform implementation
template <class T, std::invocable<T> F>
constexpr auto recursive_transform(const T& input, const F& f)
{
    return f(input);
}

template <
    std::ranges::input_range Range,
    class F>
requires (!std::invocable<F, Range>)
constexpr auto recursive_transform(const Range& input, const F& f)
{
    recursive_invoke_result_t<F, Range> output{};
    
    std::ranges::transform(
        std::ranges::cbegin(input),
        std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        [&f](auto&& element) { return recursive_transform(element, f); }
        );
    return output;
}

int main()
{
    //  std::vector<int>
    std::vector<int> test_vector = {
        1, 2, 3, 4, 5, 6
    };
    recursive_print(recursive_copy_if(test_vector, [](int x) { return (x % 2) == 0; }));

    //  std::vector<std::vector<int>>
    std::vector<decltype(test_vector)> test_vector2 = {
        test_vector, test_vector, test_vector
    };
    recursive_print(recursive_copy_if(test_vector2, [](int x) { return (x % 2) == 0; }));
    
    //  std::vector<std::string>
    recursive_print(
        recursive_copy_if(
            recursive_transform(test_vector, [](int x) { return std::to_string(x); }),
            [](std::string x) { return (x == "1"); }
        )
    );

    //  std::vector<std::vector<std::string>>
    recursive_print(
        recursive_copy_if(
            recursive_transform(test_vector2, [](int x) { return std::to_string(x); }),
            [](std::string x) { return (x == "1"); }
        )
    );

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

    recursive_print(recursive_copy_if(test_deque, [](int x) { return (x % 2) == 0; }));


    //  std::deque<std::deque<int>>
    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);

    recursive_print(recursive_copy_if(test_deque2, [](int x) { return (x % 2) == 0; }));

    //  std::list<int>
    std::list<int> test_list = { 1, 2, 3, 4, 5, 6 };

    recursive_print(recursive_copy_if(test_list, [](int x) { return (x % 2) == 0; }));


    //  std::list<std::list<int>>
    std::list<std::list<int>> test_list2 = { test_list, test_list, test_list, test_list };

    recursive_print(recursive_copy_if(test_list2, [](int x) { return (x % 2) == 0; }));
    return 0;
}

A Godbolt link is here.

All suggestions are welcome.

The summary information:

\$\endgroup\$
0
\$\begingroup\$

You don't need this line:

requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>)

And your test cases miss a std::vector<std::string> as input, but your recursive_copy_if() works fine on those as well.

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.