Группировать соседние элементы кортежа, соответствующие предикату, в поднаборы - PullRequest
2 голосов
/ 11 марта 2020

Учитывая набор некоторых элементов и предикат, как я могу сгруппировать смежные элементы, которые соответствуют предикату, в поднабор?

Пример:

#include <tuple>

auto is_float = [](auto x) {
    return std::is_same<decltype(x), float>{};
};

int main() {

    auto tup = std::tuple{1.f, 2.f, 3, 4, 5.f, 6.f, 7};
    auto result = magic_foo(is_float, tup); 
    // all adjacent floats are grouped into sub-tuples like this:
    auto expected = std::tuple{std::tuple{1.f, 2.f}, 3, 4, std::tuple{5.f, 6.f}, 7};
    assert(tup == expected);

}

1 Ответ

2 голосов
/ 11 марта 2020

Очень грубая реализация:

#include <type_traits>
#include <tuple>
#include <typeinfo>
#include <iostream>

template <typename ... T1, typename ... T2, std::size_t ... I1, std::size_t ... I2>
auto concat_tuple(const std::tuple<T1...>& t1, const std::tuple<T2...>& t2, std::index_sequence<I1...>, std::index_sequence<I2...>) {
    return std::tuple<T1..., T2...>(
        std::get<I1>(t1)...,
        std::get<I2>(t2)...
    );
}

template <typename ... T1, typename ... T2>
auto concat_tuple(const std::tuple<T1...>& t1, const std::tuple<T2...>& t2) {
    return concat_tuple(t1, t2, 
        std::make_index_sequence<sizeof...(T1)>{},
        std::make_index_sequence<sizeof...(T2)>{}
    );
}

template <typename ... T, std::size_t ... I>
auto tuple_rest(const std::tuple<T...>& tup, std::index_sequence<I...>) {
    return std::make_tuple(std::get<I+1>(tup)...);
}

template <typename ... T>
auto tuple_rest(const std::tuple<T...>& tup) {
    return tuple_rest(tup, std::make_index_sequence<sizeof...(T) - 1>{});
}

template <template <typename T> class Pred, typename ... TAcc>
auto group_by_pred(std::tuple<TAcc...> acc, std::tuple<> tup) {
    if constexpr (sizeof...(TAcc) == 0u) {
        return std::make_tuple();
    }
    else return std::make_tuple(acc);
}

template <template <typename T> class Pred, typename ... TAcc, typename T0, typename ... T>
auto group_by_pred(std::tuple<TAcc...> acc, std::tuple<T0, T...> tup) {
    if constexpr (Pred<T0>::value) {
        return group_by_pred<Pred>(
            concat_tuple(acc, std::make_tuple(std::get<0>(tup))),
            tuple_rest(tup)
        );
    }
    else {
        if constexpr (sizeof...(TAcc) == 0u) {
            return concat_tuple(
                std::make_tuple(), 
                concat_tuple(
                    std::make_tuple(std::get<0>(tup)), 
                    group_by_pred<Pred>(std::make_tuple(), tuple_rest(tup))
                )
            );
        }
        else return concat_tuple(
            std::make_tuple(acc), 
            concat_tuple(
                std::make_tuple(std::get<0>(tup)), 
                group_by_pred<Pred>(std::make_tuple(), tuple_rest(tup))
            )
        );
    }
}

template <typename T>
struct not_int : std::true_type {};

template <>
struct not_int<int> : std::false_type {};

int main() {
    auto tup = std::make_tuple(1, 2.0, 3.0, 2, 3.0);
    auto result = group_by_pred<not_int>(std::make_tuple(), tup);
    std::cout << typeid(result).name() << "\n";
}

Идея проста, но шаблонов шаблонов много.

Ссылка проводника компилятора

...