将n个向量组合成n个元组的一个向量 [英] Combining n vectors into one vector of n-tuples

查看:71
本文介绍了将n个向量组合成n个元组的一个向量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在考虑带有签名的功能

I'm thinking about a function with signature

template<typename ...Ts>
std::vector<std::tuple<Ts...>> join_vectors(std::vector<Ts>&&...) {
    //...
};

,但可能更通用的是,它接受任何可迭代的方法,而不是仅接受 std :: vector 即可.可能会有这样的签名吗?

but probably a more general one accepting any iterable instead of just std::vector would be good. Probably it would have a signature like this?

template<template<typename> typename C, typename ...Ts>
C<std::tuple<Ts...>> join_vectors(C<Ts>&&...) {
    // ...
};

但是,我还没有达到C ++的水平(尽管在Haskell中做同样的事情相对容易),因此我寻求帮助.

However, I'm not at this level yet in C++ (despite doing the same in Haskell would be relatively easy), hence I seek for help.

不幸的是,在这种情况下,我无法使用Range-v3的 zip .我对它加了标签,因为我认为对此感兴趣的人可以为我提供更好的帮助.

Unfortunately, Range-v3's zip is not at my disposal in this case. I'm tagging it because I think those interested in it are in a better position to help me.

推荐答案

对于任何具有 size 的可索引容器,可能会发生以下情况:

For any indexable containers with size something like this is possible:

#include <tuple>
#include <vector>
#include <algorithm>

// Copy from lvalue containers, move from rvalue containers.
template<typename ...Cs>
auto zip(Cs... vecs) {
    std::vector<std::tuple<typename std::decay_t<Cs>::value_type...>> vec;

    auto len = std::min({vecs.size()...});
    vec.reserve(len);
    for(std::size_t i=0;i<len;++i){
        vec.emplace_back(std::move(vecs[i])...);
    }
    return vec;
};

//Return vector of tuples with & for non-const vecs and const& if const.
template<typename ...Cs>
auto zip_view(Cs&... vecs) {
    std::vector<std::tuple<decltype(vecs[0])...>> vec;
    auto len = std::min({vecs.size()...});
    vec.reserve(len);
    for(std::size_t i=0;i<len;++i){
        vec.emplace_back(vecs[i]...);
    }
    return vec;
};

如果容器已正确实现了move构造函数,则此解决方案将复制作为左值传递的容器并从右值移动.非常轻微的缺点是,首先将整个左值容器复制,而不是仅复制单个元素.

If the containers have properly implemented move constructors, this solution will copy the containers passed as lvalues and move from rvalue ones. Very slight downside is that lvalue containers are copied whole first instead of only the individual elements.

#include <iostream>
#include <memory>
template<typename T, typename...Args>
void print_tuple(const T& first, const Args&... args){
    std::cout<<'('<<first;
    ((std::cout<<','<< args),...);
    std::cout<<')';
}

template<typename T>
struct helper{
using fnc_t = void;
};
template<typename...Args>
struct helper<std::tuple<Args...>>{
using fnc_t = void(*)(const Args&... args);
};
template<typename...Args>
struct helper<std::tuple<Args&...>>{
using fnc_t = void(*)(const Args&... args);
};

template<typename T>
using fnc_t2 = typename helper<T>::fnc_t;

template<typename T>
void template_apply(fnc_t2<T> f, const T& tuple){
    std::apply(f, tuple);
}

template<typename T>
void print_vec(const std::vector<T>& vec){
    for(const auto&e:vec){
        template_apply(print_tuple,e);
        std::cout<<'\n';
    }
}
struct MoveOnlyFoo{
    MoveOnlyFoo(int i):m_i(i){}

    int m_i;
    std::unique_ptr<int> ptr = nullptr;
};
    std::ostream& operator<<(std::ostream& o, const MoveOnlyFoo& foo){
        return o<<foo.m_i;
    }


int main(){
    std::vector v1{1,2,3,4,5,6};
    std::vector v2{'a','b','c','d','e'};
    std::vector v3{1.5,3.5,7.5};
    std::vector<MoveOnlyFoo> vmove;
    vmove.emplace_back(45);
    vmove.emplace_back(46);
    vmove.emplace_back(47);
    const std::vector v4{-1,-2,-3,-4,-5};

    //Move rvalues, copy lvalue.
    print_vec(zip(v1,v2,v3, v4, std::move(vmove)));
    // This won't work since the elements from the last vector cannot be copied.
    //print_vec(zip(v1,v2,v3, v4, vmove));
    std::cout<<"View:\n";
    //View, provides const& for const inputs, & for non-const
    print_vec(zip_view(v1,v2,v3,v4));
    std::cout<<"Modify and print:\n";
    for(auto& [x,y]: zip_view(v1,v2)){
        ++x,++y;
    }
    // Note the view can work with const containers, returns tuple of `const T&`.
    print_vec(zip_view(std::as_const(v1),std::as_const(v2)));
}

输出

(1,a,1.5,-1,45)
(2,b,3.5,-2,46)
(3,c,7.5,-3,47)
View:
(1,a,1.5,-1)
(2,b,3.5,-2)
(3,c,7.5,-3)
Modify and print:
(2,b)
(3,c)
(4,d)
(5,e)
(6,f)

请忽略打印代码的可读性;)

Please disregard the readability of the printing code ;)

我在python zip 功能之后对其进行了建模.请注意,最初的建议是复制向量,因此输出是向量,其值已从参数中移出.

I modeled it after python zip functionality. Note your initial proposal copies the vectors, so the output is a vector with the values moved from the parameters.

返回可迭代的 Cs 更加困难,因为您将必须指定如何向其中插入元素,迭代器无法自行完成.

Returning an iterable Cs is harder because you would have to specify how to insert elements into it, iterators cannot do it on their own.

让它与迭代器一起工作(但仍返回向量)是一件很麻烦的事,但从理论上讲也是可以的.

Getting it work with iterators (but returning still a vector) is a chore, but in theory also possible.

这篇关于将n个向量组合成n个元组的一个向量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆