2014-10-07 3 views
0

Я хочу реализовать функцию, которая отображает вариационный список кортежей в другой кортеж, заданный функцией.Как реализовать переменную операцию tuple_map?

Он применяет N-арную функцию f к списку элементов, взятых из списка из N кортежей (каждый с размером не менее M) и создает новый кортеж M-элементов из результата этих приложений.

Списка N кортежей, каждый из М элементов, вызов std::make_tuple будет выглядеть следующим образом псевдокода:

std::make_tuple(
    f(t1_1, t2_1, t3_1, ..., tN_1), 
    f(t1_2, t2_2, t3_2, ..., tN_2), 
    f(t1_3, t2_3, t3_3, ..., tN_3), 
    ... 
    f(t1_M, t2_M, t3_M, ..., tN_M) 
) 

Иногда эта операция называется zipWith на других языках.

Я хочу, чтобы эта функция, tuple_map, чтобы иметь следующую подпись:

template<class Tuple1, class... Tuples2, class Function> 
auto tuple_map(Tuple1&& tuple1, Tuples&&... tuples, Function f); 

Я понял, реализацию функции, принимая один кортеж:

#include <tuple> 
#include <integer_sequence> 
#include <type_traits> 
#include <utility> 

template<class Tuple, class Function, size_t... I> 
auto tuple_map_impl(Tuple&& t, Function f, std::index_sequence<I...>) 
    -> decltype(
     std::make_tuple(
     f(std::get<I>(std::forward<Tuple>(t)))... 
     ) 
    ) 
{ 
    return std::make_tuple(
    f(std::get<I>(std::forward<Tuple>(t)))... 
); 
} 

template<class Tuple, class Function> 
auto tuple_map(Tuple&& t, Function f) 
    -> decltype(
     tuple_map_impl(
     std::forward<Tuple>(t), 
     f, 
     std::make_index_sequence< 
      std::tuple_size<std::decay_t<Tuple>>::value 
     >() 
     ) 
    ) 
{ 
    using indices = std::make_index_sequence< 
    std::tuple_size<std::decay_t<Tuple>>::value 
    >; 
    return tuple_map_impl(std::forward<Tuple>(t), indices()); 
} 

Когда я ввожу другой (Tuples) в дополнение к I..., это вызывает проблемы:

template<class Tuple1, class... Tuples, class Function, size_t... I> 
auto tuple_map_impl(Tuple1&& tuple1, Tuples&&... tuples, Function f, std::index_sequence<I...>) 
    -> decltype(
     std::make_tuple(
     f(
      std::get<I>(std::forward<Tuple1>(t1)), 
      std::get<I>(std::forward<Tuples>(tuples))... 
     )... 
     ) 
    ) 
{ 
    return std::make_tuple(
    f(
     std::get<I>(std::forward<Tuple>(t)), 
     std::get<I>(std::forward<Tuples>(tuples))... 
    )... 
); 
} 

Компилятор ошибка:

error: mismatched argument pack lengths while expanding ‘get<I>(forward<Tuples>(tuples))’ 

Это потому, что я использовал две пачки с разными длинами (I и Tuples) в пределах того же выражения.

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

Каков наилучший способ реализации tuple_map?

+0

Вы можете дать полную спецификацию первых, говоря, что функция * должна * делать? –

+0

В соответствии с принятым ответом на самом деле вам нужна функция, которая * применяет функцию 'N'-ary f к списку элементов, взятых из списка кортежей' N' (каждый с размером 'M') и создает новый' M'-элементный кортеж из результата этих приложений. * –

+0

Спасибо за улов! –

ответ

4

Если я понимаю, что вы пытаетесь сделать правильно, этот код, похоже, делает трюк с Visual Studio 2013 (ноябрь 2013 CTP):

#include <iostream> 
#include <tuple> 
#include <utility> 

using namespace std; 

// index_sequence implementation since VS2013 doesn't have it yet 
template <size_t... Ints> class index_sequence { 
public: 
    static size_t size() { return sizeof...(Ints); } 
}; 

template <size_t Start, typename Indices, size_t End> 
struct make_index_sequence_impl; 

template <size_t Start, size_t... Indices, size_t End> 
struct make_index_sequence_impl<Start, index_sequence<Indices...>, End> { 
    typedef typename make_index_sequence_impl< 
     Start + 1, index_sequence<Indices..., Start>, End>::type type; 
}; 

template <size_t End, size_t... Indices> 
struct make_index_sequence_impl<End, index_sequence<Indices...>, End> { 
    typedef index_sequence<Indices...> type; 
}; 

template <size_t N> 
using make_index_sequence = 
    typename make_index_sequence_impl<0, index_sequence<>, N>::type; 

// The code that actually implements tuple_map 
template <size_t I, typename F, typename... Tuples> 
auto tuple_zip_invoke(F f, const Tuples &... ts) { 
    return f(get<I>(ts)...); 
} 

template <typename F, size_t... Is, typename... Tuples> 
auto tuple_map_impl(F f, index_sequence<Is...>, const Tuples &... ts) { 
    return make_tuple(tuple_zip_invoke<Is>(f, ts...)...); 
} 

template <typename F, typename Tuple, typename... Tuples> 
auto tuple_map(F f, const Tuple &t, const Tuples &... ts) { 
    return tuple_map_impl(f, make_index_sequence<tuple_size<Tuple>::value>(), t, 
         ts...); 
} 

int sum(int a, int b, int c) { return a + b + c; } 

int main() { 
    auto res = 
     tuple_map(sum, make_tuple(1, 4), make_tuple(2, 5), make_tuple(3, 6)); 
    cout << "(" << get<0>(res) << ", " << get<1>(res) << ")\n"; 
    return 0; 
} 

Выход:

(6, 15) 
+0

Спасибо за помощь! –

0

Я мог бы подойти к этой проблеме, факторизуя отображение и привязку. Используя существующую tuple_map, попробуйте следующее:

template <typename ...Tuple, typename F> 
auto tuple_map(Tuple && tuple, F f) 
{ 
    return std::tuple_cat(tuple_map(std::forward<Tuple>(tuple), f)...); 
} 
0

Вы можете использовать рекурсию и std::tuple_cat() так:

template <class Function, class Tuple, size_t... I> 
auto tuple_map_impl(Function f, Tuple&& t, std::index_sequence<I...>) 
{ 
    return std::make_tuple(f(std::get<I>(std::forward<Tuple>(t)))...); 
} 

template <class F, class Tuple, class... Tuples> 
auto tuple_map(F f, Tuple&& t, Tuples&&... tuples) 
{ 
    return std::tuple_cat(tuple_map_impl(f, std::forward<Tuple>(t), std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{}), 
          tuple_map(f, tuples...)); 
} 

template <class F> 
auto tuple_map(F) { return std::make_tuple(); } 

Live Demo

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