dev*_*ere 23 c++ stl stl-algorithm
我有一个整数向量:
std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
Run Code Online (Sandbox Code Playgroud)
鉴于这values.size()将永远是均匀的。
我只是想将相邻元素转换为一对,如下所示:
std::vector<std::pair<int,int>> values = { {1,2}, {3,4} , {5,6}, {7,8} ,{9,10} };
Run Code Online (Sandbox Code Playgroud)
即,两个相邻的元件连接成一对。
我可以使用什么STL算法来轻松实现这一点?是否可以通过一些标准算法来实现这一点?
当然,我可以轻松地编写一个老式的索引for循环来实现这一点。但我想知道使用基于范围的for循环或任何其他 STL 算法(例如std::transform等)最简单的解决方案是什么样的。
Cal*_*eth 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;
}
}
Run Code Online (Sandbox Code Playgroud)
zip或者,您可以通过一对strided 视图来获得类似的结果
#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;
}
}
Run Code Online (Sandbox Code Playgroud)
最后一个可以推广到 n 元组
template <size_t N>
struct tuple_chunk_t
{
template <typename R, size_t... Is>
auto impl(R && r, std::index_sequence<Is...>)
{
using namespace ranges::view;
return zip(r | drop(Is) | stride(N)...);
}
template <typename R>
auto operator()(R && r) const
{
return impl(std::forward<R>(r), std::make_index_sequence<N>{});
}
template <typename R>
friend auto operator|(R && r, chunk_t)
{
return impl(std::forward<R>(r), std::make_index_sequence<N>{});
}
};
template <size_t N>
constexpr tuple_chunk_t<N> tuple_chunk;
Run Code Online (Sandbox Code Playgroud)
And*_*dyG 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;
}
Run Code Online (Sandbox Code Playgroud)
并这样称呼它:
std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
auto result = group_pairs(values)
Run Code Online (Sandbox Code Playgroud)
for*_*818 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";
}
}
Run Code Online (Sandbox Code Playgroud)
诚然,这是否比编写普通循环更简单是值得怀疑的。
如果可能的话我会尝试不变换向量。除了访问之外,result[i].first您还可以使用values[i*2]和 类似的 for second。如果这不可行,下一个选项是std::vector<std::pair<int,int>>从一开始就填充 a,这样您就不必进行转换。首先,根据您需要的详细信息,以下可能是一个开始:
#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";
}
}
Run Code Online (Sandbox Code Playgroud)
TL;DR:考虑是否可以避免转型。如果你无法避免它,那么编写一个循环可能是最干净的。标准算法通常会有所帮助,但并非总是如此。
Pau*_*zie 11
好的,我在评论中暗示了使用std::adjacent_find,所以这里是你如何做到这一点。
是的,许多人(甚至我自己)认为这是一种黑客行为,我们使用一种用于其他用途的工具来快速解决看似不相关的问题:
#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";
}
Run Code Online (Sandbox Code Playgroud)
输出:
1 2
3 4
5 6
7 8
9 10
Run Code Online (Sandbox Code Playgroud)
它的工作方式是我们忽略第二、第四、第六等对,只保存第一、第三、第五等对。这是由boolean标志变量控制的save_it。
请注意,由于我们要处理所有对,因此std::adjacent_find谓词始终返回false。这是这个解决方案的黑客部分。