使用多个输入向量中值的笛卡尔乘积调用 lambda

Call lambda with the cartesian product of values in multiple input vectors

本文关键字:笛卡尔 lambda 调用 输入 向量      更新时间:2023-10-16

我有几个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(

这是一个简短的递归版本,仅适用于任何可迭代对象。为了简单起见,它通过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);

您使用 C++14 以便您可以使用std::index_sequence/std::make_index_sequence/std::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中提取和积累价值观。给定最终std::tuple,调用函数,使用std::getstd::tuple中提取值,并使用std::index_sequence_for生成的索引。

观察向量不需要是std::vectors;可以是std::queuestd::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_for/std::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...); }

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

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

我将调用语法从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
*/
}

现场试用

这是通过解释Barry的代码:

#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)请注意,此柯里仅在编译时可用,而不是运行时可用。