2017-11-09 15 views
2

私はタプル std::tuple<f(std::get<0>(t0),std:get<0>(t1),...),f(std::get<1>(t0),std::get<1>(t1),...),...)を返すことなど、t1、タプルt0上で機能fをマップしようとしています。 carcdr、およびconsを使用しているバージョンがありますが、std::index_sequenceを使用してバージョンを取得しようとしています。C++ 17の複数のパラメータパック展開

コード:

// Helper 
template<typename T> 
using make_tuple_index = std::make_index_sequence<std::tuple_size<T>::value>; 

// Implementation 
template<typename F, typename... Ts, std::size_t... Is> 
auto mapx_n_impl(const F& f, std::index_sequence<Is...>, const Ts&... t) 
{ return std::make_tuple(f(std::get<Is>(t...))...); } 

// Interface 
template<typename T, 
     typename F, 
     typename Indices = make_tuple_index<T>> 
auto map(const T& t, const F& f) 
{ return mapx_impl(t, f, Indices{}); } 

// Test 
auto tup1 = std::make_tuple(1.0, 2.0, 3.0); 
auto tup2 = std::make_tuple(0.0, 1.0, 2.0); 
auto r = mapx_n([](auto x, auto y) { return x - y; }, tup1, tup2); 

問題が実装return文でパラメータパックを拡大しています。私は "内側"ループのtと "外側"ループのIsを展開する必要があります。拡張はどのように制御されていますか?そして、私はreturn文をどのように修正するのですか?

UPDATE:

@Yakkからの応答との@ max66によるさらなる解明に基づいて、私は同じくらい私は可能だと思うように私のコードを簡素化しています。現在のバージョンでは、@ Yakkの答えからのパラメータパック拡張ヘルパーのバージョンと、get_element呼び出しをラムダに分解することを統合しています。

// invoke_with_pack 
template<std::size_t... Is, typename F> 
auto invoke_with_pack(std::index_sequence<Is...>, F&& function) 
{ return function(std::integral_constant<std::size_t, Is>{}...); } 

// nth 
template<natural N, typename... Ts> 
using nth = typename std::tuple_element<N, std::tuple<Ts...>>::type; 

// make_tuple_index -- Helper template for computing indices 
// corresponding to a tuple. 
template<typename T> 
using make_tuple_index = std::make_index_sequence<std::tuple_size<T>::value>; 

// map_n -- Map <function> over <tuples> t0,t1,... 
template<typename F, 
     typename... Ts, 
     typename Indices = make_tuple_index<nth<0,Ts...>>> 
auto map_n(F&& function, Ts&... tuples) 
{ 
    auto get_element = [&](auto I) { return function(std::get<I>(tuples)...); }; 
    return invoke_with_pack(Indices{}, [&](auto... Is) { 
      return std::make_tuple(get_element(Is)...); 
     }); 
} 

次に、fold_leftとfold_rightをcar、cdr、およびconsの代わりにインデックスを使用して実装する方法を説明します。これで

答えて

3

スタート:

namespace utility { 
    template<std::size_t...Is> 
    auto index_over(std::index_sequence<Is...>) { 
    return [](auto&& f)->decltype(auto) { 
     return decltype(f)(f)(std::integral_constant<std::size_t, Is>{}...); 
    }; 
    } 
    template<std::size_t N> 
    auto index_upto(std::integral_constant<std::size_t, N> ={}) { 
    return index_over(std::make_index_sequence<N>{}); 
    } 
} 

私たちはいくつかのパラメータパックを展開する機能の全体の杭を書くことを避けることができます。 index_upto<7>()([](auto...Is){ /* here */ })は、パック内のコンパイル時の整数定数0から6の束を持っているコンテキストを提供します。いくつかのコンパイラで

template<class F, class T0, class...Tuples> 
auto map_over_tuples(F&& f, T0&... t0, Tuples&&... tuples) { 
    using tuple_size = typename std::tuple_size< std::decay_t<T0> >::type; 

    auto get_element = [&](auto I){ 
    return f(std::get<I>(std::forward<T0>(t0)), std::get<I>(std::forward<Tuples>(tuples)...)); 
    }; 
    return index_upto<tuple_size{}>()([&](auto...Is){ 
    return std::make_tuple(get_element(Is)...); 
    }); 
} 

Iの使用はget_elementdecltype(I)::valueに交換しなければなりません。

+0

興味深い方法として、この例を取る - 私はそれを試してみるつもりですある時点で外に出る。あなたの答えに基づいて、以下のmax66の答えに似た実行可能な解決策を考え出すことができました。 – RandomBits

1

問題は、実装のreturn文でパラメータパックを拡張しています。 "inner"ループではtを、 "outer"ループではtを展開する必要があります。拡張はどのように制御されていますか?そして、私はreturn文をどのように修正するのですか?

私はこれを行うためのシンプルでエレガントな方法はありません。

あなたは2つのパックを同じ方法でデカップリングし、最初から最後まで拡張する必要があるようです。

Yakkソリューションが表示されている場合は、ラムダ関数の内部拡張(t...)が1回の呼び出しでf()と表示されます。

以下は、f()のコールを外に残すために、テンプレート関数と同じ原理に基づいて、std::applyを使用する解決策です。

率直に言って、私はYakkソリューションは、より効率的である(役に立たないタプルの作成の必要性を)考えていないので、パラメータパックオーバーシークエンシングのための風変わり

#include <tuple> 
#include <iostream> 

template <std::size_t I, typename ... Ts> 
auto getN (Ts const & ... t) 
{ return std::make_tuple(std::get<I>(t)...); } 

template<typename F, typename... Ts, std::size_t... Is> 
auto mapx_n_impl(const F& f, std::index_sequence<Is...>, const Ts&... t) 
{ return std::make_tuple(std::apply(f, getN<Is>(t...))...); } 

template <typename F, typename T0, typename ... Ts> 
auto mapx_n (F const & f, T0 const & t0, Ts const & ... ts) 
{ return mapx_n_impl(f, 
    std::make_index_sequence<std::tuple_size<T0>::value> {}, t0, ts...); } 

int main() 
{ 
    // Test 
    auto tup1 = std::make_tuple(1.0, 2.0, 3.0); 
    auto tup2 = std::make_tuple(0.0, 1.0, 2.0); 
    auto r = mapx_n([](auto x, auto y) { return x - y; }, tup1, tup2); 

    std::cout << std::get<0U>(r) << std::endl; 
    std::cout << std::get<1U>(r) << std::endl; 
    std::cout << std::get<2U>(r) << std::endl; 
} 
+0

これは明らかに課題を示しています。 Yakkのソリューションに基づいて、私はこのソリューションに似たものを手に入れました。これは、すでに問題に近づいてきているのできちんと合っているからです。 – RandomBits

関連する問題