Skip to main content
Update contents
Source Link
JimmyHu
  • 5.5k
  • 2
  • 10
  • 40
//  A recursive_replace_copy_if Template Function Implementation in C++

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

template<typename T>
concept is_inserterable = requires(T x)
{
    std::inserter(x, std::ranges::end(x));
};

#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
template<typename T>
concept is_multi_array = requires(T x)
{
    x.num_dimensions();
    x.shape();
    boost::multi_array(x);
};
#endif

//  recursive_depth function implementation
template<typename T>
constexpr std::size_t recursive_depth()
{
    return 0;
}

template<std::ranges::input_range Range>
constexpr std::size_t recursive_depth()
{
    return recursive_depth<std::ranges::range_value_t<Range>>() + 1;
}

//  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>
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_count implementation

//  recursive_count implementation (the version with unwrap_level)
template<std::size_t unwrap_level, class T>
constexpr auto recursive_count(const T& input, const auto& target)
{
    if constexpr (unwrap_level > 0)
    {
        static_assert(unwrap_level <= recursive_depth<T>(),
            "unwrap level higher than recursion depth of input");
        return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [&target](auto&& element) {
            return recursive_count<unwrap_level - 1>(element, target);
            });
    }
    else
    {
        return (input == target) ? 1 : 0;
    }
}

//  recursive_count implementation (the version without unwrap_level)
template<std::ranges::input_range Range>
constexpr auto recursive_count(const Range& input, const auto& target)
{
    return recursive_count<recursive_depth<Range>()>(input, target);
}

//  recursive_count implementation (with execution policy)
template<class ExPo, std::ranges::input_range Range, typename T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_count(ExPo execution_policy, const Range& input, const T& target)
{
    return std::count(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), target);
}

template<class ExPo, std::ranges::input_range Range, typename T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (std::ranges::input_range<std::ranges::range_value_t<Range>>)
constexpr auto recursive_count(ExPo execution_policy, const Range& input, const T& target)
{
    return std::transform_reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [execution_policy, target](auto&& element) {
        return recursive_count(execution_policy, element, target);
        });
}

//  recursive_count_if implementation
template<class T, std::invocable<T> Pred>
constexpr std::size_t recursive_count_if(const T& input, const Pred& predicate)
{
    return predicate(input) ? 1 : 0;
}

template<std::ranges::input_range Range, class Pred>
requires (!std::invocable<Pred, Range>)
constexpr auto recursive_count_if(const Range& input, const Pred& predicate)
{
    return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) {
        return recursive_count_if(element, predicate);
    });
}

//  recursive_count_if implementation (with execution policy)
template<class ExPo, class T, std::invocable<T> Pred>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr std::size_t recursive_count_if(ExPo execution_policy, const T& input, const Pred& predicate)
{
    return predicate(input) ? 1 : 0;
}

template<class ExPo, std::ranges::input_range Range, class Pred>
requires ((std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (!std::invocable<Pred, Range>))
constexpr auto recursive_count_if(ExPo execution_policy, const Range& input, const Pred& predicate)
{
    return std::transform_reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) {
        return recursive_count_if(element, predicate);
    });
}

//  recursive_count_if implementation (the version with unwrap_level)
template<std::size_t unwrap_level, std::ranges::range T, class Pred>
auto recursive_count_if(const T& input, const Pred& predicate)
{
    if constexpr (unwrap_level > 1)
    {
        return std::transform_reduce(std::ranges::cbegin(input), std::ranges::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(std::ranges::cbegin(input), std::ranges::cend(input), predicate);
    }
}

//  recursive_print implementation
template<typename T>
constexpr void recursive_print(const T& input, const std::size_t level = 0)
{
    std::cout << std::string(level, ' ') << input << '\n';
}

template<std::ranges::input_range Range>
constexpr void recursive_print(const Range& input, const std::size_t level = 0)
{
    std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl;"\n";
    std::ranges::for_each(input, [level](auto&& element) {
        recursive_print(element, level + 1);
    });
}

//  recursive_replace_copy_if implementation
template<std::ranges::range Range, std::invocable<std::ranges::range_value_t<Range>> UnaryPredicate, class T>
constexpr auto recursive_replace_copy_if(const Range& input, const UnaryPredicate& unary_predicate, const T& new_value)
{
    Range output{};
    std::ranges::replace_copy_if(
        std::ranges::cbegin(input),
        std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        unary_predicate,
        new_value);
    return output;
}

template<std::ranges::input_range Range, class UnaryPredicate, class T>
requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>)
constexpr auto recursive_replace_copy_if(const Range& input, const UnaryPredicate& unary_predicate, const T& new_value)
{
    Range output{};

    std::ranges::transform(
        std::ranges::cbegin(input),
        std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        [&unary_predicate, &new_value](auto&& element) { return recursive_replace_copy_if(element, unary_predicate, new_value); }
    );
    return output;
}

//  recursive_size implementation
template<class T> requires (!std::ranges::range<T>)
constexpr auto recursive_size(const T& input)
{
    return 1;
}

template<std::ranges::range Range> requires (!(std::ranges::input_range<std::ranges::range_value_t<Range>>))
constexpr auto recursive_size(const Range& input)
{
    return std::ranges::size(input);
}

template<std::ranges::range Range> requires (std::ranges::input_range<std::ranges::range_value_t<Range>>)
constexpr auto recursive_size(const Range& input)
{
    return std::transform_reduce(std::ranges::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [](auto& element) {
        return recursive_size(element);
        });
}

//  recursive_transform implementation
//  recursive_invoke_result_t implementation
//  from https://stackoverflow.com/a/65504127/6667035
template<typename//  recursive_invoke_result_t implementation
template<std::size_t, typename, typename>
struct recursive_invoke_result { };

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

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

template<typenametemplate<std::size_t unwrap_level, std::copy_constructible F, typename T>
using recursive_invoke_result_t = typename recursive_invoke_result<Frecursive_invoke_result<unwrap_level, F, T>::type;

template <std::ranges::range Range>
constexpr auto get_output_iterator(Range& output)
{
    return std::inserter(output, std::ranges::end(output));
}

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

template<std::size_t dim, class T>
constexpr auto n_dim_vector_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_vector_generator<dim - 1>(input, times);
        std::vector<decltype(element)> output(times, element);
        return output;
    }
}

template<std::size_t dim, std::size_t times, class T>
constexpr auto n_dim_array_generator(T input)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_array_generator<dim - 1, times>(input);
        std::array<decltype(element), times> output;
        std::fill(std::begin(output), std::end(output), element);
        return output;
    }
}

template<std::size_t dim, class T>
constexpr auto n_dim_deque_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_deque_generator<dim - 1>(input, times);
        std::deque<decltype(element)> output(times, element);
        return output;
    }
}

template<std::size_t dim, class T>
constexpr auto n_dim_list_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_list_generator<dim - 1>(input, times);
        std::list<decltype(element)> output(times, element);
        return output;
    }
}

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

int main()
{
    //  std::vector<int>
    std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 };
    recursive_print(recursive_replace_copy_if(test_vector, std::bind(std::less<int>(), std::placeholders::_1, 5), 55));

    //  std::vector<std::vector<int>>
    std::vector<decltype(test_vector)> test_vector2 = {
        test_vector, test_vector, test_vector
    };
    recursive_print(recursive_replace_copy_if(test_vector2, std::bind(std::less<int>(), std::placeholders::_1, 5), 55));
    
    //  std::vector<std::string>
    std::vector<std::string> test_string_vector{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" };
    recursive_print(
        recursive_replace_copy_if(
            test_string_vector, [](std::string x) { return (x == "1"); }, "11"
        )
    );

    //  std::vector<std::vector<std::string>>
    std::vector<decltype(test_string_vector)> test_string_vector2{ test_string_vector , test_string_vector , test_string_vector };
    recursive_print(
        recursive_replace_copy_if(
            test_string_vector2, [](std::string x) { return (x == "1"); }, "11"
        )
    );

    //  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_replace_copy_if(test_deque, [](int x) { return (x % 2) == 0; }, 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_replace_copy_if(test_deque2, [](int x) { return (x % 2) == 0; }, 0));

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

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


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

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

A Godbolt link is here.A Godbolt link is here.

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

template<typename T>
concept is_inserterable = requires(T x)
{
    std::inserter(x, std::ranges::end(x));
};

#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
template<typename T>
concept is_multi_array = requires(T x)
{
    x.num_dimensions();
    x.shape();
    boost::multi_array(x);
};
#endif

//  recursive_depth function implementation
template<typename T>
constexpr std::size_t recursive_depth()
{
    return 0;
}

template<std::ranges::input_range Range>
constexpr std::size_t recursive_depth()
{
    return recursive_depth<std::ranges::range_value_t<Range>>() + 1;
}

//  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>
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_count implementation

//  recursive_count implementation (the version with unwrap_level)
template<std::size_t unwrap_level, class T>
constexpr auto recursive_count(const T& input, const auto& target)
{
    if constexpr (unwrap_level > 0)
    {
        static_assert(unwrap_level <= recursive_depth<T>(),
            "unwrap level higher than recursion depth of input");
        return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [&target](auto&& element) {
            return recursive_count<unwrap_level - 1>(element, target);
            });
    }
    else
    {
        return (input == target) ? 1 : 0;
    }
}

//  recursive_count implementation (the version without unwrap_level)
template<std::ranges::input_range Range>
constexpr auto recursive_count(const Range& input, const auto& target)
{
    return recursive_count<recursive_depth<Range>()>(input, target);
}

//  recursive_count implementation (with execution policy)
template<class ExPo, std::ranges::input_range Range, typename T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_count(ExPo execution_policy, const Range& input, const T& target)
{
    return std::count(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), target);
}

template<class ExPo, std::ranges::input_range Range, typename T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (std::ranges::input_range<std::ranges::range_value_t<Range>>)
constexpr auto recursive_count(ExPo execution_policy, const Range& input, const T& target)
{
    return std::transform_reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [execution_policy, target](auto&& element) {
        return recursive_count(execution_policy, element, target);
        });
}

//  recursive_count_if implementation
template<class T, std::invocable<T> Pred>
constexpr std::size_t recursive_count_if(const T& input, const Pred& predicate)
{
    return predicate(input) ? 1 : 0;
}

template<std::ranges::input_range Range, class Pred>
requires (!std::invocable<Pred, Range>)
constexpr auto recursive_count_if(const Range& input, const Pred& predicate)
{
    return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) {
        return recursive_count_if(element, predicate);
    });
}

//  recursive_count_if implementation (with execution policy)
template<class ExPo, class T, std::invocable<T> Pred>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr std::size_t recursive_count_if(ExPo execution_policy, const T& input, const Pred& predicate)
{
    return predicate(input) ? 1 : 0;
}

template<class ExPo, std::ranges::input_range Range, class Pred>
requires ((std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (!std::invocable<Pred, Range>))
constexpr auto recursive_count_if(ExPo execution_policy, const Range& input, const Pred& predicate)
{
    return std::transform_reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) {
        return recursive_count_if(element, predicate);
    });
}

//  recursive_count_if implementation (the version with unwrap_level)
template<std::size_t unwrap_level, std::ranges::range T, class Pred>
auto recursive_count_if(const T& input, const Pred& predicate)
{
    if constexpr (unwrap_level > 1)
    {
        return std::transform_reduce(std::ranges::cbegin(input), std::ranges::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(std::ranges::cbegin(input), std::ranges::cend(input), predicate);
    }
}

//  recursive_print implementation
template<typename T>
constexpr void recursive_print(const T& input, const std::size_t level = 0)
{
    std::cout << std::string(level, ' ') << input << '\n';
}

template<std::ranges::input_range Range>
constexpr void recursive_print(const Range& input, const std::size_t level = 0)
{
    std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl;
    std::ranges::for_each(input, [level](auto&& element) {
        recursive_print(element, level + 1);
    });
}

//  recursive_replace_copy_if implementation
template<std::ranges::range Range, std::invocable<std::ranges::range_value_t<Range>> UnaryPredicate, class T>
constexpr auto recursive_replace_copy_if(const Range& input, const UnaryPredicate& unary_predicate, const T& new_value)
{
    Range output{};
    std::ranges::replace_copy_if(
        std::ranges::cbegin(input),
        std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        unary_predicate,
        new_value);
    return output;
}

template<std::ranges::input_range Range, class UnaryPredicate, class T>
requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>)
constexpr auto recursive_replace_copy_if(const Range& input, const UnaryPredicate& unary_predicate, const T& new_value)
{
    Range output{};

    std::ranges::transform(
        std::ranges::cbegin(input),
        std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        [&unary_predicate, &new_value](auto&& element) { return recursive_replace_copy_if(element, unary_predicate, new_value); }
    );
    return output;
}

//  recursive_size implementation
template<class T> requires (!std::ranges::range<T>)
constexpr auto recursive_size(const T& input)
{
    return 1;
}

template<std::ranges::range Range> requires (!(std::ranges::input_range<std::ranges::range_value_t<Range>>))
constexpr auto recursive_size(const Range& input)
{
    return std::ranges::size(input);
}

template<std::ranges::range Range> requires (std::ranges::input_range<std::ranges::range_value_t<Range>>)
constexpr auto recursive_size(const Range& input)
{
    return std::transform_reduce(std::ranges::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [](auto& element) {
        return recursive_size(element);
        });
}

//  recursive_transform implementation
//  recursive_invoke_result_t implementation
//  from https://stackoverflow.com/a/65504127/6667035
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;

template <std::ranges::range Range>
constexpr auto get_output_iterator(Range& output)
{
    return std::inserter(output, std::ranges::end(output));
}

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

template<std::size_t dim, class T>
constexpr auto n_dim_vector_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_vector_generator<dim - 1>(input, times);
        std::vector<decltype(element)> output(times, element);
        return output;
    }
}

template<std::size_t dim, std::size_t times, class T>
constexpr auto n_dim_array_generator(T input)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_array_generator<dim - 1, times>(input);
        std::array<decltype(element), times> output;
        std::fill(std::begin(output), std::end(output), element);
        return output;
    }
}

template<std::size_t dim, class T>
constexpr auto n_dim_deque_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_deque_generator<dim - 1>(input, times);
        std::deque<decltype(element)> output(times, element);
        return output;
    }
}

template<std::size_t dim, class T>
constexpr auto n_dim_list_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_list_generator<dim - 1>(input, times);
        std::list<decltype(element)> output(times, element);
        return output;
    }
}

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

int main()
{
    //  std::vector<int>
    std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 };
    recursive_print(recursive_replace_copy_if(test_vector, std::bind(std::less<int>(), std::placeholders::_1, 5), 55));

    //  std::vector<std::vector<int>>
    std::vector<decltype(test_vector)> test_vector2 = {
        test_vector, test_vector, test_vector
    };
    recursive_print(recursive_replace_copy_if(test_vector2, std::bind(std::less<int>(), std::placeholders::_1, 5), 55));
    
    //  std::vector<std::string>
    std::vector<std::string> test_string_vector{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" };
    recursive_print(
        recursive_replace_copy_if(
            test_string_vector, [](std::string x) { return (x == "1"); }, "11"
        )
    );

    //  std::vector<std::vector<std::string>>
    std::vector<decltype(test_string_vector)> test_string_vector2{ test_string_vector , test_string_vector , test_string_vector };
    recursive_print(
        recursive_replace_copy_if(
            test_string_vector2, [](std::string x) { return (x == "1"); }, "11"
        )
    );

    //  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_replace_copy_if(test_deque, [](int x) { return (x % 2) == 0; }, 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_replace_copy_if(test_deque2, [](int x) { return (x % 2) == 0; }, 0));

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

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


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

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

A Godbolt link is here.

//  A recursive_replace_copy_if Template Function Implementation in C++

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

template<typename T>
concept is_inserterable = requires(T x)
{
    std::inserter(x, std::ranges::end(x));
};

#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
template<typename T>
concept is_multi_array = requires(T x)
{
    x.num_dimensions();
    x.shape();
    boost::multi_array(x);
};
#endif

//  recursive_depth function implementation
template<typename T>
constexpr std::size_t recursive_depth()
{
    return 0;
}

template<std::ranges::input_range Range>
constexpr std::size_t recursive_depth()
{
    return recursive_depth<std::ranges::range_value_t<Range>>() + 1;
}

//  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>
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_count implementation

//  recursive_count implementation (the version with unwrap_level)
template<std::size_t unwrap_level, class T>
constexpr auto recursive_count(const T& input, const auto& target)
{
    if constexpr (unwrap_level > 0)
    {
        static_assert(unwrap_level <= recursive_depth<T>(),
            "unwrap level higher than recursion depth of input");
        return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [&target](auto&& element) {
            return recursive_count<unwrap_level - 1>(element, target);
            });
    }
    else
    {
        return (input == target) ? 1 : 0;
    }
}

//  recursive_count implementation (the version without unwrap_level)
template<std::ranges::input_range Range>
constexpr auto recursive_count(const Range& input, const auto& target)
{
    return recursive_count<recursive_depth<Range>()>(input, target);
}

//  recursive_count implementation (with execution policy)
template<class ExPo, std::ranges::input_range Range, typename T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_count(ExPo execution_policy, const Range& input, const T& target)
{
    return std::count(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), target);
}

template<class ExPo, std::ranges::input_range Range, typename T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (std::ranges::input_range<std::ranges::range_value_t<Range>>)
constexpr auto recursive_count(ExPo execution_policy, const Range& input, const T& target)
{
    return std::transform_reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [execution_policy, target](auto&& element) {
        return recursive_count(execution_policy, element, target);
        });
}

//  recursive_count_if implementation
template<class T, std::invocable<T> Pred>
constexpr std::size_t recursive_count_if(const T& input, const Pred& predicate)
{
    return predicate(input) ? 1 : 0;
}

template<std::ranges::input_range Range, class Pred>
requires (!std::invocable<Pred, Range>)
constexpr auto recursive_count_if(const Range& input, const Pred& predicate)
{
    return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) {
        return recursive_count_if(element, predicate);
    });
}

//  recursive_count_if implementation (with execution policy)
template<class ExPo, class T, std::invocable<T> Pred>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr std::size_t recursive_count_if(ExPo execution_policy, const T& input, const Pred& predicate)
{
    return predicate(input) ? 1 : 0;
}

template<class ExPo, std::ranges::input_range Range, class Pred>
requires ((std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (!std::invocable<Pred, Range>))
constexpr auto recursive_count_if(ExPo execution_policy, const Range& input, const Pred& predicate)
{
    return std::transform_reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) {
        return recursive_count_if(element, predicate);
    });
}

//  recursive_count_if implementation (the version with unwrap_level)
template<std::size_t unwrap_level, std::ranges::range T, class Pred>
auto recursive_count_if(const T& input, const Pred& predicate)
{
    if constexpr (unwrap_level > 1)
    {
        return std::transform_reduce(std::ranges::cbegin(input), std::ranges::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(std::ranges::cbegin(input), std::ranges::cend(input), predicate);
    }
}

//  recursive_print implementation
template<typename T>
constexpr void recursive_print(const T& input, const std::size_t level = 0)
{
    std::cout << std::string(level, ' ') << input << '\n';
}

template<std::ranges::input_range Range>
constexpr void recursive_print(const Range& input, const std::size_t level = 0)
{
    std::cout << std::string(level, ' ') << "Level " << level << ":" << "\n";
    std::ranges::for_each(input, [level](auto&& element) {
        recursive_print(element, level + 1);
    });
}

//  recursive_replace_copy_if implementation
template<std::ranges::range Range, std::invocable<std::ranges::range_value_t<Range>> UnaryPredicate, class T>
constexpr auto recursive_replace_copy_if(const Range& input, const UnaryPredicate& unary_predicate, const T& new_value)
{
    Range output{};
    std::ranges::replace_copy_if(
        std::ranges::cbegin(input),
        std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        unary_predicate,
        new_value);
    return output;
}

template<std::ranges::input_range Range, class UnaryPredicate, class T>
requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>)
constexpr auto recursive_replace_copy_if(const Range& input, const UnaryPredicate& unary_predicate, const T& new_value)
{
    Range output{};

    std::ranges::transform(
        std::ranges::cbegin(input),
        std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        [&unary_predicate, &new_value](auto&& element) { return recursive_replace_copy_if(element, unary_predicate, new_value); }
    );
    return output;
}

//  recursive_size implementation
template<class T> requires (!std::ranges::range<T>)
constexpr auto recursive_size(const T& input)
{
    return 1;
}

template<std::ranges::range Range> requires (!(std::ranges::input_range<std::ranges::range_value_t<Range>>))
constexpr auto recursive_size(const Range& input)
{
    return std::ranges::size(input);
}

template<std::ranges::range Range> requires (std::ranges::input_range<std::ranges::range_value_t<Range>>)
constexpr auto recursive_size(const Range& input)
{
    return std::transform_reduce(std::ranges::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [](auto& element) {
        return recursive_size(element);
        });
}

//  recursive_transform implementation
//  recursive_invoke_result_t implementation
//  from https://stackoverflow.com/a/65504127/6667035
//  recursive_invoke_result_t implementation
template<std::size_t, typename, typename>
struct recursive_invoke_result { };

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

template<std::size_t unwrap_level, std::copy_constructible F, template<typename...> typename Container, typename... Ts>
requires (std::ranges::input_range<Container<Ts...>> &&
        requires { typename recursive_invoke_result<unwrap_level - 1, F, std::ranges::range_value_t<Container<Ts...>>>::type; })
struct recursive_invoke_result<unwrap_level, F, Container<Ts...>>
{
    using type = Container<typename recursive_invoke_result<unwrap_level - 1, F, std::ranges::range_value_t<Container<Ts...>>>::type>;
};

template<std::size_t unwrap_level, std::copy_constructible F, typename T>
using recursive_invoke_result_t = typename recursive_invoke_result<unwrap_level, F, T>::type;

template <std::ranges::range Range>
constexpr auto get_output_iterator(Range& output)
{
    return std::inserter(output, std::ranges::end(output));
}

template<std::size_t dim, class T>
constexpr auto n_dim_vector_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_vector_generator<dim - 1>(input, times);
        std::vector<decltype(element)> output(times, element);
        return output;
    }
}

template<std::size_t dim, std::size_t times, class T>
constexpr auto n_dim_array_generator(T input)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_array_generator<dim - 1, times>(input);
        std::array<decltype(element), times> output;
        std::fill(std::begin(output), std::end(output), element);
        return output;
    }
}

template<std::size_t dim, class T>
constexpr auto n_dim_deque_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_deque_generator<dim - 1>(input, times);
        std::deque<decltype(element)> output(times, element);
        return output;
    }
}

template<std::size_t dim, class T>
constexpr auto n_dim_list_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_list_generator<dim - 1>(input, times);
        std::list<decltype(element)> output(times, element);
        return output;
    }
}

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

int main()
{
    //  std::vector<int>
    std::vector<int> test_vector{ 5, 7, 4, 2, 8, 6, 1, 9, 0, 3 };
    recursive_print(recursive_replace_copy_if(test_vector, std::bind(std::less<int>(), std::placeholders::_1, 5), 55));

    //  std::vector<std::vector<int>>
    std::vector<decltype(test_vector)> test_vector2 = {
        test_vector, test_vector, test_vector
    };
    recursive_print(recursive_replace_copy_if(test_vector2, std::bind(std::less<int>(), std::placeholders::_1, 5), 55));
    
    //  std::vector<std::string>
    std::vector<std::string> test_string_vector{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" };
    recursive_print(
        recursive_replace_copy_if(
            test_string_vector, [](std::string x) { return (x == "1"); }, "11"
        )
    );

    //  std::vector<std::vector<std::string>>
    std::vector<decltype(test_string_vector)> test_string_vector2{ test_string_vector , test_string_vector , test_string_vector };
    recursive_print(
        recursive_replace_copy_if(
            test_string_vector2, [](std::string x) { return (x == "1"); }, "11"
        )
    );

    //  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_replace_copy_if(test_deque, [](int x) { return (x % 2) == 0; }, 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_replace_copy_if(test_deque2, [](int x) { return (x % 2) == 0; }, 0));

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

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


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

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

A Godbolt link is here.

Add template tag
Link
JimmyHu
  • 5.5k
  • 2
  • 10
  • 40

The output of the testing code above:

Level 0:
 5
 7
 55
 55
 8
 6
 55
 9
 55
 55
Level 0:
 Level 1:
  5
  7
  55
  55
  8
  6
  55
  9
  55
  55
 Level 1:
  5
  7
  55
  55
  8
  6
  55
  9
  55
  55
 Level 1:
  5
  7
  55
  55
  8
  6
  55
  9
  55
  55
Level 0:
 Level 1:
  0
 Level 1:
  1
  1
 Level 1:
  2
 Level 1:
  3
 Level 1:
  4
 Level 1:
  5
 Level 1:
  6
 Level 1:
  7
 Level 1:
  8
 Level 1:
  9
 Level 1:
  1
  0
 Level 1:
  1
  1
 Level 1:
  1
  2
 Level 1:
  1
  3
 Level 1:
  1
  4
 Level 1:
  1
  5
 Level 1:
  1
  6
 Level 1:
  1
  7
 Level 1:
  1
  8
 Level 1:
  1
  9
 Level 1:
  2
  0
Level 0:
 Level 1:
  Level 2:
   0
  Level 2:
   1
   1
  Level 2:
   2
  Level 2:
   3
  Level 2:
   4
  Level 2:
   5
  Level 2:
   6
  Level 2:
   7
  Level 2:
   8
  Level 2:
   9
  Level 2:
   1
   0
  Level 2:
   1
   1
  Level 2:
   1
   2
  Level 2:
   1
   3
  Level 2:
   1
   4
  Level 2:
   1
   5
  Level 2:
   1
   6
  Level 2:
   1
   7
  Level 2:
   1
   8
  Level 2:
   1
   9
  Level 2:
   2
   0
 Level 1:
  Level 2:
   0
  Level 2:
   1
   1
  Level 2:
   2
  Level 2:
   3
  Level 2:
   4
  Level 2:
   5
  Level 2:
   6
  Level 2:
   7
  Level 2:
   8
  Level 2:
   9
  Level 2:
   1
   0
  Level 2:
   1
   1
  Level 2:
   1
   2
  Level 2:
   1
   3
  Level 2:
   1
   4
  Level 2:
   1
   5
  Level 2:
   1
   6
  Level 2:
   1
   7
  Level 2:
   1
   8
  Level 2:
   1
   9
  Level 2:
   2
   0
 Level 1:
  Level 2:
   0
  Level 2:
   1
   1
  Level 2:
   2
  Level 2:
   3
  Level 2:
   4
  Level 2:
   5
  Level 2:
   6
  Level 2:
   7
  Level 2:
   8
  Level 2:
   9
  Level 2:
   1
   0
  Level 2:
   1
   1
  Level 2:
   1
   2
  Level 2:
   1
   3
  Level 2:
   1
   4
  Level 2:
   1
   5
  Level 2:
   1
   6
  Level 2:
   1
   7
  Level 2:
   1
   8
  Level 2:
   1
   9
  Level 2:
   2
   0
Level 0:
 1
 0
 3
 0
 5
 0
Level 0:
 Level 1:
  1
  0
  3
  0
  5
  0
 Level 1:
  1
  0
  3
  0
  5
  0
 Level 1:
  1
  0
  3
  0
  5
  0
Level 0:
 1
 0
 3
 0
 5
 0
Level 0:
 Level 1:
  1
  0
  3
  0
  5
  0
 Level 1:
  1
  0
  3
  0
  5
  0
 Level 1:
  1
  0
  3
  0
  5
  0
 Level 1:
  1
  0
  3
  0
  5
  0

The output of the testing code above:

Level 0:
 5
 7
 55
 55
 8
 6
 55
 9
 55
 55
Level 0:
 Level 1:
  5
  7
  55
  55
  8
  6
  55
  9
  55
  55
 Level 1:
  5
  7
  55
  55
  8
  6
  55
  9
  55
  55
 Level 1:
  5
  7
  55
  55
  8
  6
  55
  9
  55
  55
Level 0:
 Level 1:
  0
 Level 1:
  1
  1
 Level 1:
  2
 Level 1:
  3
 Level 1:
  4
 Level 1:
  5
 Level 1:
  6
 Level 1:
  7
 Level 1:
  8
 Level 1:
  9
 Level 1:
  1
  0
 Level 1:
  1
  1
 Level 1:
  1
  2
 Level 1:
  1
  3
 Level 1:
  1
  4
 Level 1:
  1
  5
 Level 1:
  1
  6
 Level 1:
  1
  7
 Level 1:
  1
  8
 Level 1:
  1
  9
 Level 1:
  2
  0
Level 0:
 Level 1:
  Level 2:
   0
  Level 2:
   1
   1
  Level 2:
   2
  Level 2:
   3
  Level 2:
   4
  Level 2:
   5
  Level 2:
   6
  Level 2:
   7
  Level 2:
   8
  Level 2:
   9
  Level 2:
   1
   0
  Level 2:
   1
   1
  Level 2:
   1
   2
  Level 2:
   1
   3
  Level 2:
   1
   4
  Level 2:
   1
   5
  Level 2:
   1
   6
  Level 2:
   1
   7
  Level 2:
   1
   8
  Level 2:
   1
   9
  Level 2:
   2
   0
 Level 1:
  Level 2:
   0
  Level 2:
   1
   1
  Level 2:
   2
  Level 2:
   3
  Level 2:
   4
  Level 2:
   5
  Level 2:
   6
  Level 2:
   7
  Level 2:
   8
  Level 2:
   9
  Level 2:
   1
   0
  Level 2:
   1
   1
  Level 2:
   1
   2
  Level 2:
   1
   3
  Level 2:
   1
   4
  Level 2:
   1
   5
  Level 2:
   1
   6
  Level 2:
   1
   7
  Level 2:
   1
   8
  Level 2:
   1
   9
  Level 2:
   2
   0
 Level 1:
  Level 2:
   0
  Level 2:
   1
   1
  Level 2:
   2
  Level 2:
   3
  Level 2:
   4
  Level 2:
   5
  Level 2:
   6
  Level 2:
   7
  Level 2:
   8
  Level 2:
   9
  Level 2:
   1
   0
  Level 2:
   1
   1
  Level 2:
   1
   2
  Level 2:
   1
   3
  Level 2:
   1
   4
  Level 2:
   1
   5
  Level 2:
   1
   6
  Level 2:
   1
   7
  Level 2:
   1
   8
  Level 2:
   1
   9
  Level 2:
   2
   0
Level 0:
 1
 0
 3
 0
 5
 0
Level 0:
 Level 1:
  1
  0
  3
  0
  5
  0
 Level 1:
  1
  0
  3
  0
  5
  0
 Level 1:
  1
  0
  3
  0
  5
  0
Level 0:
 1
 0
 3
 0
 5
 0
Level 0:
 Level 1:
  1
  0
  3
  0
  5
  0
 Level 1:
  1
  0
  3
  0
  5
  0
 Level 1:
  1
  0
  3
  0
  5
  0
 Level 1:
  1
  0
  3
  0
  5
  0
Update Godbolt link
Source Link
JimmyHu
  • 5.5k
  • 2
  • 10
  • 40
Loading
Update description
Source Link
JimmyHu
  • 5.5k
  • 2
  • 10
  • 40
Loading
Update code block content
Source Link
JimmyHu
  • 5.5k
  • 2
  • 10
  • 40
Loading
Update question description
Source Link
JimmyHu
  • 5.5k
  • 2
  • 10
  • 40
Loading
Source Link
JimmyHu
  • 5.5k
  • 2
  • 10
  • 40
Loading