如何使用 STL 算法将 std::vector<T> 转换为 std::vector<std::pair<T,T>> 对向量?

问题描述 投票:0回答:5

我有一个整数向量:

std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};

鉴于

values.size()
永远是偶数。

我只是想将相邻元素转换为一对,如下所示:

std::vector<std::pair<int,int>> values = { {1,2}, {3,4} , {5,6}, {7,8} ,{9,10} };

即,两个相邻元素连接成一对。

我可以使用什么STL算法来轻松实现这一点?是否可以通过一些标准算法来实现这一点?

当然,我可以轻松地编写一个老式索引 for 循环来实现这一点。但我想知道使用基于范围的 for 循环或任何其他 STL 算法(如

std::transform
等)最简单的解决方案是什么样的。

c++ stl stl-algorithm
5个回答
22
投票

一旦我们有了 C++23 对

<ranges>
的扩展,您就可以使用
std::ranges::views::chunk
来实现大部分功能,尽管这会产生子范围,而不是对。

#include <iostream>
#include <ranges>
#include <vector>

int main()
{
    std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
    auto chunk_to_pair = [](auto chunk)
    {
        return std::pair(*chunk.begin(), *std::next(chunk.begin()));
    };
    for (auto [first, second] : values | std::ranges::views::chunk(2) | std::ranges::views::transform(chunk_to_pair))
    {
        std::cout << first << second << std::endl;
    }
}

或者,您可以通过

zip
处理一对
stride
d 视图来实现类似的结果

#include <iostream>
#include <ranges>
#include <vector>

int main()
{
    std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
    auto odds = values | std::ranges::views::drop(0) | std::ranges::views::stride(2);
    auto evens = values | std::ranges::views::drop(1) | std::ranges::views::stride(2);
    for (auto [first, second] : std::ranges::views::zip(odds, evens))
    {
        std::cout << first << second << std::endl;
    }
}

最后一个可以推广到 n 元组

template <size_t N>
struct tuple_chunk_t : std::ranges::range_adaptor_closure<tuple_chunk_t<N>>
{
    template <std::ranges::viewable_range R>
    auto operator()(R && r) const
    {
        auto impl = []<size_t... Is>(R && r, std::index_sequence<Is...>)
        {
            return std::views::zip(std::forward<R>(r) | std::views::drop(Is) | std::views::stride(N)...);
        };
        return impl(std::forward<R>(r), std::make_index_sequence<N>{});
    }
};

template <size_t N>
constexpr tuple_chunk_t<N> tuple_chunk;

Coliru 链接


14
投票

我不确定为什么你需要一个标准算法,而你自己编写的算法大约有 5 行代码(加上样板):

template<class T>
std::vector<std::pair<T, T>> group_pairs(const std::vector<T>& values)
{
    assert(values.size() % 2 == 0);
    auto output = std::vector<std::pair<T, T>>();
    output.reserve(values.size()/2);
    for(size_t i = 0; i < values.size(); i+=2)
        output.emplace_back(values[i], values[i+1]);
    return output;
}

并这样称呼它:

std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
auto result = group_pairs(values)

现场演示


12
投票

我不知道有哪个标准算法可以直接执行您想要的操作(尽管我对 C++20 及更高版本不是很熟悉)。您始终可以编写一个循环,并且大多数循环都可以通过

std::for_each
来表示,这是一种标准算法。


当你成对积累元素时,我会尝试一下:

std::accumulate

这是否比编写普通循环更简单是值得怀疑的。

如果可能的话我会尝试不变换向量。除了访问
#include <vector> #include <numeric> #include <iostream> struct pair_accumulator { std::vector<std::pair<int,int>> result; int temp = 0; bool set = false; pair_accumulator& operator+(int x){ if (set) { result.push_back({temp,x}); set = false; } else { temp = x; set = true; } return *this; } }; int main() { std::vector<int> values = {1,2,3,4,5,6,7,8,9,10}; auto x = std::accumulate(values.begin(),values.end(),pair_accumulator{}).result; for (const auto& e : x) { std::cout << e.first << " " << e.second << "\n"; } }

,您还可以使用

result[i].first
以及类似的
values[i*2]
。如果这不可行,下一个选项是从一开始就填充
second
,这样您就不必进行转换。首先,根据您需要的详细信息,以下可能是一个开始:
std::vector<std::pair<int,int>>

TL;DR:考虑是否可以避免转型。如果你无法避免它,那么编写一个循环可能是最干净的。标准算法通常会有所帮助,但并非总是如此。


11
投票
std::adjacent_find

,所以这就是你如何做到这一点。 是的,许多人(甚至我自己)认为这是一种黑客行为,我们使用一种用于其他用途的工具来快速解决看似不相关的问题:

#include <vector> #include <iostream> struct view_as_pairs { std::vector<int>& values; struct proxy { std::vector<int>::iterator it; int& first() { return *it;} int& second() { return *(it +1); } }; proxy operator[](size_t index){ return proxy{values.begin() + index*2}; } size_t size() { return values.size() / 2;} }; int main() { std::vector<int> values = {1,2,3,4,5,6,7,8,9,10}; view_as_pairs v{values}; for (size_t i=0; i < v.size(); ++i){ std::cout << v[i].first() << " " << v[i].second() << "\n"; } }

输出:

#include <algorithm> #include <iostream> #include <utility> #include <vector> int main() { //Test data std::vector<int> v = {1,2,3,4,5,6,7,8,9,10}; // results std::vector<std::pair<int,int>> result; // save flag bool save_it = true; // Use std::adjacent_find std::adjacent_find(v.begin(), v.end(), [&](int n1, int n2) { if (save_it) result.push_back({n1,n2}); save_it = !save_it; return false; }); for (auto& pr : result) std::cout << pr.first << " " << pr.second << "\n"; }

它的工作方式是我们忽略第二、第四、第六等对,只保存第一、第三、第五等对。  这是由 
1 2 3 4 5 6 7 8 9 10

标志变量

boolean
控制的。
请注意,由于我们要处理所有对,因此 

save_it

谓词始终返回

std::adjacent_find
。 这是这个解决方案的黑客部分。
    


3
投票
false

迭代器直接作为算法的输入。定义一个返回

std::vector
且步幅为 2 的自定义迭代器怎么样?创建向量对向量就是使用
std::pair
的单行代码。迭代器有效地提供了对原始向量的“视图”。这也允许使用许多标准算法。下面的示例也可以进行相当多的推广,以适用于大多数容器迭代器,即,您只需完成一次定义此类迭代器的艰巨工作,然后就可以将其应用于各种容器和算法。实例:
https://godbolt.org/z/ceEsvKhzd
std::copy

	
© www.soinside.com 2019 - 2024. All rights reserved.