代码之家  ›  专栏  ›  技术社区  ›  Steve Lorimer

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

  •  6
  • Steve Lorimer  · 技术社区  · 6 年前

    我有几个向量 int s或 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);
        });
    
    • 输入向量的数目是可变的
    • 输入向量中存储的类型是可变的

    4 回复  |  直到 6 年前
        1
  •  6
  •   Barry    6 年前

    下面是一个简短的递归版本,它只适用于任何iterables。它把一切都带走了 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);
    
        2
  •  3
  •   max66    6 年前

    你使用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...);
     }
    

    所以 简单地变成

    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::元组 ,调用从 使用 std::get 以及用 标准::索引序列 .

    std::vector s、 可能是 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::索引\U序列 / / 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...); }
    
        3
  •  2
  •   HolyBlackCat    6 年前

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

    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

        4
  •  0
  •   Joseph Mariadassou    6 年前

    #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...) 创建curried函数 f c1 . 所以什么时候 cs 变为空,所有参数都将变为curried f() process(1,1.0,2)