C++系列十:日常学习-范围库Ranges

发布时间 2023-10-09 20:08:44作者: cactus9

前言

不错麽

内容参考

  1. https://zh.cppreference.com/w/cpp/ranges
  2. Chatjpt

总结注意点:

  1. 确保你的C++编译器支持C++20标准
  2. 包含 ranges 头文件
  3. views的操作是惰性的,它们不会立即执行,而是在需要时计算。这意味着你可以构建复杂的管道,而不必担心性能问题。提供性能优势,因为它们避免了不必要的数据拷贝和临时存储。在处理大型数据集时,这种优化尤为重要。
  4. 使用views不会修改原始数据,它们只是提供了对数据的查看和操作方式。因此,原始数据保持不变,不会被修改。
  5. 要小心处理可能的异常情况,例如范围越界或无效的操作。可以使用try-catch块来处理异常。
  6. 管道操作符 |(竖线符号)=>数据处理操作可以从左到右依次执行,

介绍

C++20引入了Ranges库,其中的views是该库的一个关键组成部分。views提供了一种现代化的、功能强大的方法来处理数据序列,它基于"range-based"编程范式,允许你以声明性的方式对数据序列进行操作,而无需手动编写循环。以下是关于views的详细介绍:

  1. 什么是views

    views是C++20中Ranges库的一部分,它代表了一个数据序列的虚拟视图。这个视图允许你以一种便捷和现代的方式访问和操作数据,同时不需要实际地拷贝或修改原始数据。views的操作是惰性的,只有在需要的时候才会执行。

  2. 视图的创建:

    你可以通过调用标准容器的views成员函数或使用适配器来创建视图。例如,可以使用std::vector创建一个视图:

    std::vector<int> numbers = {1, 2, 3, 4, 5};
    auto view = numbers | std::views::transform([](int x) { return x * 2; });
    

    在这里,std::views::transform是一个视图适配器,它将一个函数应用于容器中的每个元素,创建一个新的视图。

  3. 视图的操作:

    一旦创建了视图,你可以对其执行各种操作,这些操作包括但不限于:

    • 筛选(filtering):筛选出满足特定条件的元素。

    • 映射(mapping):将函数应用于序列中的每个元素。

    • 切片(slicing):选择范围内的元素。

    • 排序(sorting):对元素进行排序。

    你可以将多个操作组合在一起来创建复杂的数据处理管道,而这些操作都是惰性执行的。

  4. 遍历视图:

    一旦创建了视图,你可以使用for循环或范围for循环来遍历它。视图会在遍历时动态计算元素,而不会一次性生成所有元素。

    for (int num : view) {       std::cout << num << " ";   }
    
  5. 性能优势:

    views具有优化的性能。它们避免了不必要的数据拷贝和临时存储,使得在处理大型数据集时非常高效。

  6. 自定义视图:

    你还可以创建自定义的视图,通过实现必要的迭代器和操作符来定义你自己的数据序列操作。

  7. views的应用场景:

    views适用于各种应用场景,包括数据转换、筛选、数据流处理、映射、集合操作等。它们可以使你的代码更加清晰、可读和高效。

总之,views是C++20引入的一个重要特性,它使得序列处理变得更加简单和现代化。它们提供了一种优雅的方式来处理数据序列,避免了繁琐的手动迭代和数据操作。在使用时,请确保你的编译器支持C++20标准并了解如何正确使用Ranges库以发挥其强大的功能。

举例:

std::vector<int> numbers = {1, 2, 3, 4, 5};
// 使用views::transform将每个元素翻倍=>2 4 6 8 10
auto doubled = numbers | std::views::transform([](int x) { return x * 2; });
// 使用views::filter筛选偶数 =>2 4
auto evenNumbers = numbers | std::views::filter([](int x) { return x % 2 == 0; });
// 使用views::slice选择范围[1, 3)=> 2 3 4
auto slicedRange = numbers | std::views::slice(1, 4);
// 链式操作:筛选偶数,然后翻倍=>4 8
auto result = numbers | std::views::filter([](int x) { return x % 2 == 0; })
                      | std::views::transform([](int x) { return x * 2; });
// 使用views::iota生成范围[1, 10)=>1~9
auto sequence = std::views::iota(1, 10);
// 生成无限递增序列~~~
auto infiniteSequence = std::views::iota(1);   
// 使用views::reverse反向迭代容器=>5 4 3 2 1
auto reversed = numbers | std::views::reverse;    
// 使用views::concat合并两个容器
std::vector<int> numbers1 = {1, 2, 3};
std::vector<int> numbers2 = {4, 5, 6};
auto combined = std::views::concat(numbers1, numbers2);
for (int num : doubled) {
  std::cout << num << " "; 
//拆分字符串
std::string text = "Hello,World,C++,Ranges";
auto splitView = text | std::views::split(',');
for (const auto& range : splitView) {
    std::string token(range.begin(), range.end());
    std::cout << token << std::endl;
} 

//对比:
auto const ints = {0,1,2,3,4,5};
auto even = [](int i) { return 0 == i % 2; };
auto square = [](int i) { return i * i; };
// 组合视图的“管道”语法:
for (int i : ints | std::views::filter(even) | std::views::transform(square)) {
    std::cout << i << ' ';
}
std::cout << '\n';
// 传统的“函数式”组合语法:
for (int i : std::views::transform(std::views::filter(ints, even), square)) {
    std::cout << i << ' ';
}


还有许多适配器就不一一举例说明了。