C++20 ranges/view库简介

C++20 引入了 ranges 库,这是标准模板库 (STL) 的一个重大改进。ranges 库引入了一种新的方式来处理序列(如数组、向量等),使代码更加简洁和表达性更强。ranges/view 是 ranges 库中的一个重要组成部分,专门用于创建和操作视图(view)。以下是对 C++20 中 ranges/view 的简要介绍:

ranges 库的核心概念

  1. Ranges:范围(range)是指一个有起点和终点的序列,可以通过迭代器访问。
  2. Views:视图(view)是一个轻量级的、非拥有的范围,它可以对原始数据进行各种操作(如过滤、变换)而不复制数据。
  3. Actions:动作(action)是对范围进行的更改或操作,通常会返回一个新的容器。

ranges/view 的特点

  1. 惰性计算:视图是惰性计算的,这意味着只有在实际访问数据时才会执行操作。这使得视图非常高效,特别是对于大数据集。
  2. 组合操作:视图可以通过管道(|)运算符进行组合,允许多次操作链式调用,生成更复杂的视图。
  3. 无副作用:视图不会修改原始数据,只会在需要时生成一个新的视图。

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}

参考链接

  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值