2014-11-25 3 views
5

Ответы here и here - это в значительной степени то, что мне нужно. Тем не менее, я хотел бы иметь возможность генерировать последовательности, такие как:Компиляция - генерация целочисленной последовательности с одним оставленным

gen_seq<5, 2> // {0, 1, 3, 4} 
gen_seq<3, 0> // {1, 2} 
// optional behavior that would be useful for me: 
gen_seq<4, 4> // {0, 1, 2, 3} 

В примерах я использовал gen_seq, чтобы сформировать последовательность от 0 до N-1 без I. Это не является обязательным, я бы также хорошо с gen_seq, где N - длина последовательности, а I - отсутствующий индекс или другие варианты.

Я думаю, что большая часть проблемы уже ответила на связанные вопросы. Однако я не могу обернуть голову тем, как включить условие «оставьте это» для второго параметра.

В идеале я хотел бы придерживаться особенностей C++ 11 и избегать C++ 14. Однако элегантные и особенно читаемые души, использующие C++ 14, также могут быть очень интересными.

ответ

7

Вы можете использовать следующее:

#if 1 // Not in C++11 // make_index_sequence 
#include <cstdint> 

template <std::size_t...> struct index_sequence {}; 

template <std::size_t N, std::size_t... Is> 
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {}; 

template <std::size_t... Is> 
struct make_index_sequence<0u, Is...> : index_sequence<Is...> { using type = index_sequence<Is...>; }; 

#endif // make_index_sequence 

namespace detail 
{ 
    template <typename Seq1, std::size_t Offset, typename Seq2> struct concat_seq; 

    template <std::size_t ... Is1, std::size_t Offset, std::size_t ... Is2> 
    struct concat_seq<index_sequence<Is1...>, Offset, index_sequence<Is2...>> 
    { 
     using type = index_sequence<Is1..., (Offset + Is2)...>; 
    }; 
} 

template <std::size_t N, std::size_t E> 
using gen_seq = typename detail::concat_seq<typename make_index_sequence<E>::type, E + 1, typename make_index_sequence<(N > E) ? (N - E - 1) : 0>::type>::type; 

static_assert(std::is_same<index_sequence<0, 1, 3, 4>, gen_seq<5, 2>>::value, ""); 
static_assert(std::is_same<index_sequence<1, 2>, gen_seq<3, 0>>::value, ""); 
static_assert(std::is_same<index_sequence<0, 1, 2, 3>, gen_seq<4, 4>>::value, ""); 

Live example

1

Всегда здорово написать свои вопросы.

Я только что понял, что могу просто использовать метод divide и conquer и генерировать не от 0 до N/2 и N/2 + 1 до n, а на первом этапе генерировать от 0 до I-1 и от I + 1 до N.

Это, я могу комбинировать с методом генерации линейной или также логарифмической глубины. Теперь я чувствую себя глупо за вопрос, но, по крайней мере, я больше не застрял.

2

Простой линейный метод генерации целочисленных последовательностей тривиальным адаптируется к за исключением конкретных пунктов, путем добавления специализации, которая охватывает случай, когда деталь исключение:

#include <iostream> 

// general case, ignores X 

template <int N, int X, int... vals> 
struct gen_seq : gen_seq<N - 1, X, N - 1, vals...> { }; 

template <int X, int... vals> 
struct gen_seq<0, X, vals...> { static constexpr int values[] = { vals... }; }; 

// specialisations when vals has had X prepended: remove X 

template <int N, int X, int... vals> 
struct gen_seq<N, X, X, vals...> : gen_seq<N, X, vals...> { }; 

template <int... vals> 
struct gen_seq<0, 0, 0, vals...> : gen_seq<0, 0, vals...> { }; 

template <int X, int... vals> 
constexpr int gen_seq<0, X, vals...>::values[]; 

int main() { 
    for (auto i : gen_seq<5, 2>::values) std::cout << i << std::endl; // 0 1 3 4 
    for (auto i : gen_seq<3, 0>::values) std::cout << i << std::endl; // 1 2 
    for (auto i : gen_seq<4, 4>::values) std::cout << i << std::endl; // 0 1 2 3 
} 

Это может быть не так эффективно, как другие более сложные методы, но это наиболее читаемые. В отличие от вашего собственного ответа и Jarod42, это не создает новый алгоритм поверх уже существующего, но строит новый с нуля.

Смежные вопросы