文章目录
C++20 引入了 ranges 库,这是标准模板库 (STL) 的一个重大改进。ranges 库引入了一种新的方式来处理序列(如数组、向量等),使代码更加简洁和表达性更强。ranges/view 是 ranges 库中的一个重要组成部分,专门用于创建和操作视图(view)。以下是对 C++20 中 ranges/view 的简要介绍:
ranges 库的核心概念
- Ranges:范围(range)是指一个有起点和终点的序列,可以通过迭代器访问。
- Views:视图(view)是一个轻量级的、非拥有的范围,它可以对原始数据进行各种操作(如过滤、变换)而不复制数据。
- Actions:动作(action)是对范围进行的更改或操作,通常会返回一个新的容器。
ranges/view 的特点
- 惰性计算:视图是惰性计算的,这意味着只有在实际访问数据时才会执行操作。这使得视图非常高效,特别是对于大数据集。
- 组合操作:视图可以通过管道(
|
)运算符进行组合,允许多次操作链式调用,生成更复杂的视图。 - 无副作用:视图不会修改原始数据,只会在需要时生成一个新的视图。
ranges/view 的使用
过滤操作
可以使用 filter
操作过滤序列中的元素,
#include <fmt/ranges.h>
#include <ranges>
#include <vector>
int main() {
std::vector vec = {1, 2, 3, 4, 5, 6};
auto even_view = vec | std::views::filter([](int n) { return n % 2 == 0; });
fmt::println("even numbers: {}", even_view); // even numbers: [2, 4, 6]
}
变换操作
可以使用 transform
操作对每个元素应用一个函数。
#include <fmt/ranges.h>
#include <ranges>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
auto square_view = vec | std::views::transform([](int n) { return n * n; });
fmt::println("square numbers: {}", square_view); // square numbers: [1, 4, 9, 16, 25]
return 0;
}
截取操作
take
操作可以从序列的开头截取指定数量的元素。drop
操作可以丢弃指定数量的元素。
#include <fmt/ranges.h>
#include <ranges>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
auto first_three_view = vec | std::views::take(3);
auto drop_three_view = vec | std::views::drop(3);
fmt::println("take first three: {}", first_three_view); // take first three: [1, 2, 3]
fmt::println("drop first three: {}", drop_three_view); // drop first three: [4, 5]
return 0;
}
zip操作
zip
操作可以将两个序列合并为一个元素对序列。
#include <fmt/ranges.h>
#include <ranges>
#include <vector>
int main() {
std::vector v1{1, 2, 3, 4};
std::vector v2{'a', 'b', 'c', 'd'};
auto r1{std::views::zip(v1, v2)};
fmt::println("r1: {}", r1); // r1: [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]
return 0;
}
更多的操作
类似Python的枚举接口
std::vector vec = {'a', 'b', 'c', 'd', 'e'};
auto r = vec | std::views::enumerate;
for (auto [i, c] : r) {
fmt::println("index: {}, value: {}", i, c);
}
访问key-value对
std::vector<std::pair<int, std::string>> vec = {
{1, "Ken"}, {2, "Ben"}, {3, "Alan"}};
auto keys = vec | std::views::keys;
auto values = vec | std::views::values;
fmt::println("keys: {}", keys); // keys: [1, 2, 3]
fmt::println("values: {}", values); // values: [Ken, Ben, Alan]
访问 tuple 的元素:
std::vector<std::tuple<int, std::string, std::string>> vec = {
{1, "Ken", "Briton"}, {2, "Ben", "USA"}, {3, "Alan", "Canada"}};
auto addr = vec | std::views::elements<2>;
fmt::println("addr: {}", addr); // addr: [Briton, USA, Canada]
分割操作
使用split
操作可以将字符串分割为子字符串。
std::string str = "Hello, World!";
auto r15 = str | std::views::split(',');
fmt::println("{}", r15); // [['H', 'e', 'l', 'l', 'o'],
// [' ', 'W', 'o', 'r', 'l', 'd', '!']]
合并操作
使用join
操作可以将子字符串合并为一个字符串。
{
vector vs{"hi"s, "you"s, "!"s};
auto str{vs | std::views::join};
fmt::println("{}", str); // ['h', 'i', 'y', 'o', 'u', '!']
}
{
vector vs{"hi"s, "you"s, "!"s};
auto str{vs | std::views::join_with('\n')};
fmt::println("{}", str); // ['h', 'i', '\n', 'y', 'o', 'u', '\n', '!']
}
zip 操作
zip
操作可以将两个序列合并为一个元素对序列。
{
vector v1{1, 2, 3};
vector v2{'a', 'b', 'c'};
auto r1{std::views::zip(v1, v2)};
fmt::println("r1: {}", r1); // [(1, 'a'), (2, 'b'), (3, 'c')]
}
{
vector v1{1, 2, 3};
vector v2{4, 5, 6};
auto r2{std::views::zip_transform(std::multiplies(), v1, v2)};
fmt::println("r2: {}", r2); // [4, 10, 18]
}
滑动窗口操作
slide
操作可以生成一个滑动窗口序列。
vector v{1, 2, 3, 4, 5};
auto r7{v | std::views::slide(2)};
fmt::println("r7: {}", r7); // [[1, 2], [2, 3], [3, 4], [4, 5]]
adjacent
操作与此有点像, 可以生成一个相邻元素对序列。
vector v{1, 2, 3, 4};
auto r3{v | std::views::adjacent<2>};
fmt::println("r3: {}", r3); // [(1, 2), (2, 3), (3, 4)]
分块访问
chunk
操作可以将序列分块。注意chunk之间没有重叠。
{
vector v{1, 2, 3, 4, 5};
auto r8{v | std::views::chunk(2)};
fmt::println("r8: {}", r8); // [[1, 2], [3, 4], [5]]
}
{
vector v{1, 2, 2, 3, 0, 4, 5, 2};
auto r9{v | std::views::chunk_by(std::ranges::less_equal{})};
// explain:
// 1 < 2 <= 2 < 3;
// 0 < 4 <= 5;
// 2
fmt::println("r9: {}", r9); // {(1,2,2,3),(0,4,5),(2)}
}
间隔访问
stride
操作可以按照指定的步长访问序列。
vector v{1, 2, 3, 4, 5};
auto r10{v | std::views::stride(2)};
fmt::println("{}", r10); // {1, 3, 5}