Вызовите лямбда с декартовым произведением значений во множественных входных векторах - PullRequest
0 голосов
/ 07 сентября 2018

У меня есть несколько векторов: int с или double с:

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 ]

0 голосов
/ 10 сентября 2018

Это объяснение кода Барри:

#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) Обратите внимание, что это карри доступно только во время компиляции, а не во время выполнения.

0 голосов
/ 07 сентября 2018

Вы используете 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::tuple с использованием std::get и индексы, сгенерированные с помощью std::index_sequence_for.

Заметьте, что нет необходимости, чтобы вектор был std::vector с; может быть 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_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...); }
0 голосов
/ 07 сентября 2018

Вот короткая рекурсивная версия, которая просто работает с любыми итерациями. Для простоты нужно все на 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);
0 голосов
/ 07 сентября 2018

Вот мое решение. Это, вероятно, не оптимально, но это работает.

Недостатком является то, что он работает только с контейнерами с произвольным доступом.

Я изменил синтаксис вызова с 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
    */
}

Попробуй вживую

...