简体   繁体   English

从 C++ 中的单个向量创建对向量

[英]Create a vector of pairs from a single vector in C++

I have a single even-sized vector that I want to transform into a vector of pairs where each pair contains always two elements.我有一个偶数大小的向量,我想将其转换为成对向量,其中每对总是包含两个元素。 I know that I can do this using simple loops but I was wondering if there is a nice standard-library tool for this?我知道我可以使用简单的循环来做到这一点,但我想知道是否有一个很好的标准库工具呢? It can be assumed that the original vector always contains an even amount of elements.可以假设原始向量总是包含偶数个元素。

Example:例子:

vector<int> origin {1, 2, 3, 4, 5, 6, 7, 8};

vector<pair<int, int>> goal { {1, 2}, {3, 4}, {5, 6}, {7, 8} };

Use Range-v3:使用范围-v3:

#include <range/v3/range/conversion.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/chunk.hpp>

using namespace ranges;
using namespace ranges::views;

int main() {
    std::vector<int> origin {1, 2, 3, 4, 5, 6, 7, 8};
    std::vector<std::pair<int, int>> goal { {1, 2}, {3, 4}, {5, 6}, {7, 8} };

    auto constexpr makePairFromRangeOf2 = [](auto two){
        return std::make_pair(two.front(), two.back());
    };

    auto result = origin | chunk(2)
                         | transform(makePairFromRangeOf2)
                         | to_vector;
}

Notice that if you only have to loop on result , then you only need it to be a range, so you can leave | to_vector请注意,如果您只需要循环result ,那么您只需要它是一个范围,所以您可以离开| to_vector | to_vector out, because you'll still be able to do result.begin() and result.end() , which is what makes result a range. | to_vector out,因为您仍然可以执行result.begin()result.end() ,这就是使result成为范围的原因。

If you don't need the inner containers to truly be std::pair s, but your just happy with calling, say, result.front().front() instead of result.front().first , then you can leave also the transform , and just be happy with auto result = origin | chunk(2);如果您不需要内部容器真正成为std::pair s,但您只是对调用result.front().front()而不是result.front().first ,那么您可以离开也是transform ,并且对auto result = origin | chunk(2);感到满意auto result = origin | chunk(2); . .

You don't mention why you only want a standard solution.你没有提到为什么你只想要一个标准的解决方案。 However consider that <ranges> is standard in C++20.但是请考虑<ranges>在 C++20 中标准的。 Unfortunately that feature is not as powerful as pre-C++20 Range-v3 library.不幸的是,该功能不如 C++20 之前的 Range-v3 库强大。 But it will be at some point (C++23?), I think without any doubts.但它会在某个时候(C++23?),我认为毫无疑问。

As mentioned by @康桓瑋 , if you're willing to also use the ranges-v3 library, you can use a chunk() view:正如@康桓玮所提到的,如果您愿意也使用 range-v3 库,您可以使用chunk()视图:

std::vector origin = {1, 2, 3, 4, 5, 6, 7, 8};
auto goal = v | ranges::views::chunk(2) | ranges::to<std::vector>;

See it working on GodBolt .看到它在GodBolt上工作。

Unlike my other answer , this will be perfectly valid language-wise.与我的其他答案不同,这在语言方面将是完全有效的。

Caveats:注意事项:

  • This will make a copy of your data!这将复制您的数据!
  • Likely to introduce a bunch of stuff (error strings, exception handlers etc.) into your object code.可能会在您的目标代码中引入一堆东西(错误字符串、异常处理程序等)。
  • The ranges library increases compilation times significantly (although perhaps less so with C++20 enabled?)范围库显着增加了编译时间(尽管启用 C++20 的情况可能更少?)
  • Not based on the standard library - but apparently chunk() and to() will be in C++23, so upto a slight syntax tweak (eg adding std:: ), this will be valid C++23 with only standard library includes.不基于标准库-但显然chunk()to()将在 C++23 中,因此只需稍作语法调整(例如添加std:: ),这将是有效的 C++23,只有标准库包括。
  • The elements of goal are not std::pair s, bur rather ranges. goal的元素不是std::pair s,而是范围。 You will need to get the first and second, or first and last, elements to make an actual pair.您将需要获取第一个和第二个,或第一个和最后一个元素来组成一个实际的对。

I have a function for handling both even and odd number of elements in a vector.我有一个处理向量中偶数和奇数元素的函数。 What it does that it takes another parameter to add a number at the end of pair.它的作用是需要另一个参数在对的末尾添加一个数字。 I don't think there's any standard tool/library to do so as of C++ 20 , there is Range-v3 library which will be in C++ 23 which isn't released yet.我认为从 C++ 20 开始没有任何标准工具/库可以这样做, C++ 20 C++ 23Range-v3库,但尚未发布。

Here is the try it online link.这是在线试用链接。

#include <iostream>
#include <vector>

// time complexity: O(n / 2), where `n` is the length of `my_vec`
std::vector<std::pair<int, int>> vec_to_pair(const std::vector<int> &my_vec, int odd_origin)
{
    std::vector<std::pair<int, int>> val;
    for (std::size_t i = 0; i < my_vec.size(); i += 2)
    {
        int sec_val;
        if (i < my_vec.size() - 1)
            sec_val = my_vec[i + 1];
        else if (my_vec.size() % 2 != 0)
            sec_val = odd_origin;
        else 
            break;
        int data[] = {my_vec[i], sec_val};
        val.push_back({data[0], data[1]});
    }
    return val;
}

void print(const std::vector<std::pair<int, int>> &vec)
{
    std::cout << "{ ";
    for (auto &&i : vec)
        std::cout << "{ " << i.first << ", " << i.second << " }  ";
    std::cout << " }" << std::endl;
}

int main(void)
{
    std::vector<int> vec1 = {1, 2, 3, 4, 5};    // odd
    std::vector<int> vec2 = {1, 2, 3, 4, 5, 6}; // even

    auto x1 = vec_to_pair(vec1, -1);
    auto x2 = vec_to_pair(vec2, 0);

    print(x1);
    print(x2);

    return 0;
}

Simple way of doing this without using the range v3 library:不使用 range v3 库的简单方法:

template <typename T>
std::vector<std::pair<T, T>> windowed(const std::vector<T> &vec) {
    const size_t size = vec.size();
    if (size % 2 != 0) {
        throw std::exception("Vector does not contain an even amount of elements!");
    }
    
    std::vector<std::pair<T, T>> result;
    for (size_t i = 0; i < size; i = i + 2) {
        const T &left = vec.at(i);
        const T &right = vec.at(i + 1);
        result.emplace_back(left, right);
    }
    return result;
}

The intuitive, but unfortunately invalid, way to do it直观但不幸的是无效的方法

There's a quick-and-dirty approach, which will kinda-hopefully-maybe do what you asked for, and will not even copy the data at all... but the downside is that you can't be certain it will work.有一种快速而肮脏的方法,它可能会按照您的要求进行,甚至根本不会复制数据......但缺点是您不能确定它会起作用。 It relies on undefined behavior , and can thus not be recommended.它依赖于未定义的行为,因此不推荐。 I'm describing it because I believe it's what one imagines, intuitively, that we might be able to do.我正在描述它,因为我相信这是人们直观地想象的,我们可能能够做到的事情。

So, it's about using std::span with re-interpretation of the vector data:所以,它是关于使用std::span重新解释矢量数据:

std::vector<int> origin {1, 2, 3, 4, 5, 6, 7, 8};
auto raw_data = reinterpret_cast<std::pair<int, int>*>(origin.data());
std::span<std::pair<int, int>> goal { raw_data, origin.size()/2 };

See this on GodBoltGodBolt上看到这个

Caveats:注意事项:

  • reinterpret_cast is "dirty". reinterpret_cast是“脏的”。 It officially results in undefined behavior, and what it does in practice depends on the compiler and platform.它正式导致未定义的行为,它在实践中的作用取决于编译器和平台。 You can circumvent this if you also forget about std::pair 's, and instead use a 2-dimensional mdspan instead.如果您也忘记了std::pair ,则可以避免这种情况,而是使用二维mdspan Of course, mdspan 's are not in the standard library.当然, mdspan不在标准库中。
  • This is C++20.这是 C++20。 Before C++20 you can still use span's, but they're not in the standard library.在 C++20 之前,您仍然可以使用 span,但它们不在标准库中。
  • Specifically, if you're on a platform where unaligned accesses are not allowed, in may be a problem to insist on there being twice-the-size-of-int values at goal.data() .具体来说,如果您在一个不允许未对齐访问的平台上,那么坚持在goal.data()中存在两倍于 int 的值可能是一个问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM