6

我有几个ints 或doubles 的向量:

std::vector<int>    iv = { 1, 2, 3, 4 };
std::vector<double> jv = { .5, 1., 1.5, 2. };
std::vector<int>    kv = { 5, 4, 3, 2 };

我需要处理每个向量的笛卡尔积:

for (int i : iv)
{
    for (double j : jv)
    {
        for (int k : kv)
        {
            process(i, j, k);
        }
    }
}

我想把它扁平化成一个电话

product(iv, jv, kv, [=](int i, double j, int k)
    {
        process(i, j, k);
    });
  • 输入向量的数量是可变的
  • 存储在输入向量中的类型是可变的

这可能吗?(我正在使用 C++14)

4

4 回答 4

6

这是一个简短的递归版本,仅适用于任何可迭代对象。为简单起见,它需要一切const&

template <typename F>
void product(F f) {
    f();
}

template <typename F, typename C1, typename... Cs> 
void product(F f, C1 const& c1, Cs const&... cs) {
    for (auto const& e1 : c1) {
        product([&](auto const&... es){
            f(e1, es...);
        }, cs...);
    }   
}

这将是:

product(process, iv, jv, kv);
于 2018-09-07T20:22:37.547 回答
3

您使用 C++14,因此您可以使用std::index_sequence// ...std::make_index_sequencestd::index_sequence_for

我建议先调用product()函数,然后调用向量。

我是说

 product([=](int i, double j, int k) { process(i, j, k); }, iv, jv, kv);

这样您就可以将可变参数模板用于向量。

我还建议以下辅助功能

template <typename F, std::size_t ... Is, typename Tp>
void productH (F f, std::index_sequence<Is...> const &, Tp const & tp)
 { f(std::get<Is>(tp)...); }

template <typename F, typename Is, typename Tp, typename T0, typename ... Ts>
void productH (F f, Is const & is, Tp const & tp, T0 const & t0, Ts ... ts)
 { 
   for ( auto const & val : t0 )
      productH(f, is, std::tuple_cat(tp, std::tie(val)), ts...);
 }

所以product()简单地变成

template <typename F, typename ... Ts>
void product (F f, Ts ... ts)
 { productH(f, std::index_sequence_for<Ts...>{}, std::make_tuple(), ts...); }

这个想法是提取和积累std::tuple价值。给定 final ,调用从using和用 生成的索引std::tuple中提取值的函数。std::tuplestd::getstd::index_sequence_for

观察到向量没有必要是std::vectors;可以是std::queue,std::array

下面是一个完整的编译示例

#include <array>
#include <tuple>
#include <deque>
#include <vector>
#include <utility>
#include <iostream>

template <typename F, std::size_t ... Is, typename Tp>
void productH (F f, std::index_sequence<Is...> const &, Tp const & tp)
 { f(std::get<Is>(tp)...); }

template <typename F, typename Is, typename Tp, typename T0, typename ... Ts>
void productH (F f, Is const & is, Tp const & tp, T0 const & t0, Ts ... ts)
 { 
   for ( auto const & val : t0 )
      productH(f, is, std::tuple_cat(tp, std::tie(val)), ts...);
 }

template <typename F, typename ... Ts>
void product (F f, Ts ... ts)
 { productH(f, std::index_sequence_for<Ts...>{}, std::make_tuple(), ts...); }

void process (int i1, double d1, int i2)
 { std::cout << '[' << i1 << ',' << d1 << ',' << i2 << ']' << std::endl; }

int main ()
 {
   std::vector<int>       iv = { 1, 2, 3, 4 };
   std::array<double, 4u> jv = { { .5, 1., 1.5, 2. } };
   std::deque<int>        kv = { 5, 4, 3, 2 };

   product([=](int i, double j, int k) { process(i, j, k); }, iv, jv, kv);
 }

不幸的是,你不能使用 C++17 ,你可以避免std::index_sequence//部分并使用如下std::index_sequence_forstd::get()std::apply()

template <typename F, typename Tp>
void productH (F f, Tp const & tp)
 { std::apply(f, tp); }

template <typename F, typename Tp, typename T0, typename ... Ts>
void productH (F f, Tp const & tp, T0 const & t0, Ts ... ts)
 { 
   for ( auto const & val : t0 )
      productH(f, std::tuple_cat(tp, std::tie(val)), ts...);
 }

template <typename F, typename ... Ts>
void product (F f, Ts ... ts)
 { productH(f, std::make_tuple(), ts...); }
于 2018-09-07T18:29:00.737 回答
2

这是我的解决方案。它可能不是最佳的,但它有效。

一个缺点是它仅适用于随机访问容器。

我将调用语法从 更改为product(a, b, c, lambda)product(a, b, c)(lambda)因为这个更容易实现。

#include <cstddef>
#include <iostream>
#include <vector>
#include <utility>

template <typename ...P, std::size_t ...I>
auto product_impl(std::index_sequence<I...>, const P &...lists)
{
    return [&lists...](auto &&func)
    {
        std::size_t sizes[]{lists.size()...};
        std::size_t indices[sizeof...(P)]{};
        std::size_t i = 0;

        while (i != sizeof...(P))
        {
            func(lists[indices[I]]...);

            for (i = 0; i < sizeof...(P); i++)
            {
                indices[i]++;
                if (indices[i] == sizes[i])
                    indices[i] = 0;
                else
                    break;
            }
        }
    };
}

template <typename ...P>
auto product(const P &...lists)
{
    return product_impl(std::make_index_sequence<sizeof...(P)>{}, lists...);
}

int main()
{
    std::vector<int> a = {1,2,3};
    std::vector<float> b = {0.1, 0.2};
    std::vector<int> c = {10, 20};

    product(a, b, c)([](int x, float y, int z)
    {
        std::cout << x << "  " << y << "  " << z << '\n';
    });

    /* Output:
    1  0.1  10
    2  0.1  10
    3  0.1  10
    1  0.2  10
    2  0.2  10
    3  0.2  10
    1  0.1  20
    2  0.1  20
    3  0.1  20
    1  0.2  20
    2  0.2  20
    3  0.2  20
    */
}

Try it live

于 2018-09-07T18:19:45.990 回答
0

这是通过解释巴里的代码:

#include <iostream>
template <typename F>
void product(F f) {
    f();
}

template <typename F, typename C1, typename... Cs> 
void product(F f, C1 const& c1, Cs const&... cs) {
       product([&] ( auto const&... es){ 
            f(c1,es...);
          },
       cs...);
}

void process(int i, double j, int k)
{
  std::cout << i << " " << j << " " << k << std::endl;
}

int main()
{
   product(process, 1, 1.0, 2);
}

这只是一种奇特的调用方式process.。关键是f(c1,es...)创建一个柯里化函数,f其中第一个参数被锁定在c1. 因此,当cs变为空时,所有参数都被柯里化并f()登陆调用process(1,1.0,2)注意,这种柯里化仅在编译时可用,而不是在运行时可用。

于 2018-09-09T23:56:37.010 回答