Вызов лямбды с декартовым произведением значений в нескольких входных векторах

У меня есть несколько векторов 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)

Вы это видели, это хоть как-то помогает: stackoverflow.com/questions/44206965/…?

storaged 07.09.2018 19:45
product(std::tuple<...>, functor ); должно быть возможно
Slava 07.09.2018 19:45

Как насчет ericniebler.github.io/range-v3/…

Slava 07.09.2018 19:49
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
3
310
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

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

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

Вы используете 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...); }

Проголосовали за использование итераторов (через ранжирование для) вместо индексов, как это сделал я.

HolyBlackCat 07.09.2018 21:00

Это круто! Спасибо!

Steve Lorimer 07.09.2018 21:01
Ответ принят как подходящий

Вот короткая рекурсивная версия, которая работает с любыми итерациями. Для простоты от 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++ 11 ...

Walter 08.09.2018 01:46

@Walter Какая часть вам нужна? Если вы попытаетесь устно объяснить кому-нибудь, как составлять декартово произведение, вы, вероятно, в конечном итоге получите именно такой алгоритм. Вы можете сделать это и в C++ 11, просто нужно написать признак типа, чтобы перейти от типа контейнера к константной ссылке.

Barry 08.09.2018 04:27

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

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

Другие вопросы по теме