C++初学者指南-5.标准库(第二部分)--可组合范围视图

C++初学者指南-5.标准库(第二部分)–可组合范围视图


不熟悉 C++ 的标准库算法? ⇒ 简介

请注意,C++23 的例子需要最新的编译器(例如 g++13)才能运行,并且一些工具如 ranges::to 或 views::enumerate 可能尚不可用。

介绍

<ranges> Library

返回视图对象的工厂
std::views::NAME (args…) → VIEW-OBJECT

  • 像函数一样调用(通常作为函数对象实现)
  • 创建视图的首选方式,但并非对所有视图类型都可用

在这里插入图片描述
cppreference

#include <ranges>
std::vector<int> v {7,2,6,3,4};
for (int x : std::views::reverse(v)) { cout << x <<' '; } // 4 3 6 2 7
// using pipeline notation:
for (int x : v | std::views::reverse) { cout << x <<' '; } // 4 3 6 2 7
// storing a view in a variable:
auto rv = std::views::reverse(v);
// 'rv' has type 'std::ranges::reverse_view'
for (int x : rv) { cout << x <<' '; } // 4 3 6 2 7

运行示例代码

视图类型
std::ranges::NAME_view {args…}

#include <ranges>
std::vector<int> v {7,2,6,3,4};
std::ranges::reverse_view rv {v};
for (int x : rv) { cout << x <<' '; } // 4 3 6 2 7

运行示例代码

组合视图

可以用管道符号 | 将只接受单个输入范围(可能还有一些其他非范围参数)的视图连起来进行联动:
view1 | view2 | view3 | …
示例1
在这里插入图片描述

#include <ranges>
std::vector<int> input {1,3,4,2,5,2,7,8};
auto const is_even = [] (int x) { return !(x & 1); };
auto result = input
  | std::views::reverse
  | std::views::filter(is_even);
for (int x : result) { cout << x <<' '; } // 8 2 2 4

运行示例代码

示例2
在这里插入图片描述

#include <ranges>
namespace views = std::views; // alias
std::vector<int> input {1,3,2,2,0,1,0,8,5,0};
auto const greater_0 = [] (int x) { return x > 0; };
auto const plus_1 = [] (int x) { return x + 1; };
auto result = input
  | views::drop(2)
  | views::filter(greater_0)
  | views::transform(plus_1);
for (int x : result) { cout << x <<' '; } // 3 3 2 9 6

运行示例代码

示例3
在这里插入图片描述

#include <ranges>
namespace views = std::views; // alias
std::vector<int> input {8,7,3,3,9};
auto result = views::zip(input, views::iota(1))
  | views::reverse;
fmt::print("{}\n", result);
// [(9,5), (3,4), (3,3), (7,2), (8,1)]

运行示例代码

转换为容器

在这里插入图片描述
cppreference
std::ranges::to 在最新的 g++/libstdc++ 中可能还不可用。
示例1
在这里插入图片描述
示例2
在这里插入图片描述

元素选择器

返回一个包含输入范围中单个元素的范围

filter

在这里插入图片描述
cppreference

#include <ranges>
std::vector<int> v {7,2,6,3,4};
// custom predicate:
auto const is_even = [] (int x) { return !(x & 1); };
fmt::print("{}\n", std::views::filter(v,is_even)); // [2,6,4]
// using pipeline notation:
fmt::print("{}\n", v | std::views::filter(is_even)); // [2,6,4]
// using an explicit view object:
std::ranges::filter_view fv {v,is_even};
fmt::print("{}\n", fv); // [2,6,4]

运行示例代码

stride

在这里插入图片描述
cppreference

#include <ranges>
std::vector<int> v {0,1,2,3,4,5,6,7,8};
fmt::print("{}\n", std::views::stride(v,2)); // [0,2,4,6,8]
fmt::print("{}\n", std::views::stride(v,3)); // [0,3,6]
// using pipeline notation:
fmt::print("{}\n", v | std::views::stride(2)); // [0,2,4,6,8]
// using an explicit view object:
std::ranges::stride_view sv2 {v,2};
fmt::print("{}\n", sv2); // [0,2,4,6,8]

运行示例代码

子范围选择器

返回输入范围的连续子范围

counted

在这里插入图片描述
cppreference

#include <ranges>
std::vector<int> v {9,1,3,7,1,2,5,8};
fmt::print("{}\n", std::views::counted(v.begin()+2,4)); // [3,7,1,2]

运行示例代码
请注意,std::views::counted 不能用于管道操作,并且没有 std::ranges::counted_view 类型。

take

在这里插入图片描述
cppreference

#include <ranges>
std::vector<int> v {0,1,2,3,4};
fmt::print("{}\n", std::views::take(v,3)); // [0,1,2]
// using pipeline notation:
fmt::print("{}\n", v | std::views::take(3)); // [0,1,2]
// using an explicit view object:
std::ranges::take_view tv {v,3};
fmt::print("{}\n", tv); // [0,1,2]

运行示例代码

drop

在这里插入图片描述
cppreference

#include <ranges>
std::vector<int> v {0,1,2,3,4};
fmt::print("{}\n", std::views::drop(v,2)); // [2,3,4]
// using pipeline notation:
fmt::print("{}\n", v | std::views::drop(2)); // [2,3,4]
// using an explicit view object:
std::ranges::drop_view dv {v,2};
fmt::print("{}\n", dv); // [2,3,4]

运行示例代码

take_while

在这里插入图片描述
cppreference

#include <ranges>
std::vector<int> v {2,6,4,3,8};
// custom predicate:
auto const is_even = [] (int x) { return !(x & 1); };
fmt::print("{}\n", std::views::take_while(v,is_even)); // [2,6,4]
// using pipeline notation:
fmt::print("{}\n", v | std::views::take_while(is_even)); // [2,6,4]
// using an explicit view object:
std::ranges::take_while_view tv {v,is_even};
fmt::print("{}\n", tv); // [2,6,4]

运行示例代码

drop_while

在这里插入图片描述
cppreference

#include <ranges>
std::vector<int> v {2,6,7,6,5};
// custom predicate:
auto const is_even = [] (int x) { return !(x & 1); };
fmt::print("{}\n", std::views::drop_while(v,is_even)); // [7,6,5]
// using pipeline notation:
fmt::print("{}\n", v | std::views::drop_while(is_even)); // [7,6,5]
// using an explicit view object:
std::ranges::drop_while_view dv {v,is_even};
fmt::print("{}\n", dv); // [7,6,5]

运行示例代码

滑动窗口视图

返回输入范围内的一系列元组或连续子范围。

slide

在这里插入图片描述
cppreference

#include <ranges>
std::vector<int> v {0,1,2,3,4};
for (auto x : std::views::slide(v,2)) { fmt::print("{} ",x); }
// [0,1] [1,2] [2,3] [3,4]
for (auto x : std::views::slide(v,3)) { fmt::print("{} ",x); }
// [0,1,2] [1,2,3] [2,3,4]
// using pipeline notation:
for (auto x : v | std::views::slide(3)) { fmt::print("{} ",x); }
// [0,1,2] [1,2,3] [2,3,4]
// using an explicit view object:
std::ranges::slide_view sv3 {v,3};
for (auto x : sv3) { fmt::print("{} ",x); }
// [0,1,2] [1,2,3] [2,3,4]

运行示例代码

adjacent

在这里插入图片描述
cppreference

#include <ranges>
std::string s = "abcde";
for (auto p : std::views::adjacent<3>(s)) {fmt::print("{} ",p); } // abc bcd cde
// using pipeline notation:
for (auto p : s | std::views::adjacent<3>) {fmt::print("{} ",p); } // abc bcd cde
for (auto p : s | std::views::pairwise) {fmt::print("{} ",p); } // ab bc cd de

运行示例程序

函数应用

transform

在这里插入图片描述
cppreference

#include <ranges>
#include <cctype>  // std::toupper
std::string s = "abcd";
// function to be applied:
auto const to_upper = [] (char c) { 
    return static_cast<char>(std::toupper(static_cast<unsigned char>(c))); 
};
fmt::print("{}\n", std::views::transform(s,to_upper)); // [A,B,C,D]
// using pipeline notation:
fmt::print("{}\n", s | std::views::transform(to_upper)); // [A,B,C,D]
// using an explicit view object:
std::ranges::transform_view fv {s,to_upper};
fmt::print("{}\n", fv); // [A,B,C,D]

运行示例代码

adjacent_transform

在这里插入图片描述
cppreference

#include <ranges>
std::string s = "abcde";
auto const f3 = [] (char x, char y, char z) { return std::string{'<',x,y,z,'>'}; };
for (auto p : std::views::adjacent_transform<3>(s,f3)) { fmt::print("{} ",p); }
// <abc> <bcd> <cde>
auto const f2 = [] (char x, char y) { return std::string{'<',x,y,'>'}; };
for (auto p : std::views::pairwise_transform(s,f2)) { fmt::print("{} ",p); }
// <ab> <bc> <cd> <de>
// using pipeline notation:
for (auto p : s | std::views::pairwise_transform(f2)) { fmt::print("{} ",p); }
// <ab> <bc> <cd> <de>

运行示例代码

标记化视图

返回输入范围的一系列连续子范围

split

在这里插入图片描述
cppreference

#include <ranges>
std::vector<int> v {2,6,1,0,9,5,3,1,0,7};
for (auto x : std::views::split(v,1)) { fmt::print("{} ",x); }
// [2,6] [0,9,5,3] [0,7]
std::array<int,2> s {1,0};
for (auto x : std::views::split(v,s)) { fmt::print("{} ",x); }
// [2,6] [9,5,3] [7]
// using pipeline notation:
for (auto x : v | std::views::split(1)) { fmt::print("{} ",x); }
// [2,6] [0,9,5,3] [0,7]
// using an explicit view object:
std::ranges::split_view dv {v,1};
for (auto x : dv) { fmt::print("{} ",x); }
// [2,6] [0,9,5,3] [0,7]

运行示例代码

lazy_split

在这里插入图片描述
cppreference
与split主要的区别在于 lazy_split 只有在通过 iterator 访问 lazy_split 返回的范围对象时,才会生成输出元素。

#include <ranges>
std::vector<int> v {2,6,1,0,9,5,3,1,0,7};
for (auto x : std::views::lazy_split(v,1)) { fmt::print("{} ",x); }
// [2,6] [0,9,5,3] [0,7]
std::array<int,2> s {1,0};
for (auto x : std::views::lazy_split(v,s)) { fmt::print("{} ",x); }
// [2,6] [9,5,3] [7]
// using pipeline notation:
for (auto x : v | std::views::lazy_split(1)) { fmt::print("{} ",x); }
// [2,6] [0,9,5,3] [0,7]
// using an explicit view object:
std::ranges::lazy_split_view dv {v,1};
for (auto x : dv) { fmt::print("{} ",x); }
// [2,6] [0,9,5,3] [0,7]

运行示例代码

chunk

在这里插入图片描述
cppreference

#include <ranges>
std::vector<int> v {0,1,2,3,4,5,6,7};
for (auto x : std::views::chunk(v,2)) { fmt::print("{} ",x); }
// [0,1] [2,3] [4,5] [6,7]
for (auto x : std::views::chunk(v,3)) { fmt::print("{} ",x); }
// [0,1,2] [3,4,5] [6,7]
// using pipeline notation:
for (auto x : v | std::views::chunk(3)) { fmt::print("{} ",x); }
// [0,1,2] [3,4,5] [6,7]
// using an explicit view object:
std::ranges::chunk_view sv3 {v,3};
for (auto x : sv3) { fmt::print("{} ",x); }
// [0,1,2] [3,4,5] [6,7]

运行示例代码

chunk_by

在这里插入图片描述
cppreference

#include <ranges>
std::vector<int> v {1,2,0,2,4,5,8,4,6,3,5,2,4};
for (auto x : std::views::chunk_by(v,std::ranges::less{})) { fmt::print("{} ",x); }
// [1,2] [0,2,4,5,8] [4,6] [3,5] [2,4]
// custom predicate:
auto const diff_less3 = [] (int x, int y) { return std::abs(x-y) < 3; };
for (auto x : std::views::chunk_by(v,diff_less3)) { fmt::print("{} ",x); }
// [1,2,0,2,4,5] [8] [4,6] [3,5] [2,4]
for (auto x : std::views::chunk_by(v,std::ranges::less{})) { fmt::print("{} ",x); }
// [1,2] [0,2,4,5,8] [4,6] [3,5] [2,4]
// using pipeline notation:
for (auto x : v | std::views::chunk_by(std::ranges::less{})) { fmt::print("{} ",x); }
// [1,2] [0,2,4,5,8] [4,6] [3,5] [2,4]
// using an explicit view object:
std::ranges::chunk_by_view sv3 {v,std::ranges::less{}};
for (auto x : sv3) { fmt::print("{} ",x); }
// [1,2] [0,2,4,5,8] [4,6] [3,5] [2,4]

运行示例代码

多重范围 → 单一范围

join

在这里插入图片描述
cppreference

#include <ranges>
std::vector<std::string> v = {"I","am","here","now"};
fmt::print("{}\n", std::views::join(v)); // Iamherenow
// using pipeline notation:
fmt::print("{}\n", v | std::views::join); // Iamherenow
// using an explicit view object:
std::ranges::join_view fv {v};
fmt::print("{}\n", fv); // Iamherenow

运行示例代码

join_with

在这里插入图片描述
cppreference

#include <ranges>
std::vector<std::string> v = {"I","am","here"};
fmt::print("{}\n", std::views::join_with(v,'#')); // I#am#here
std::vector<std::vector<int>> a {{7},{3,3,7},{2,6}};
std::vector<int> b {0,1};
fmt::print("{}\n", std::views::join_with(a,b)); // 7 0 1 3 3 7 0 1 2 6
// using pipeline notation:
fmt::print("{}\n", v | std::views::join_with('#')); // I#am#here
// using an explicit view object:
std::ranges::join_with_view fv {v,'#'};
fmt::print("{}\n", fv); // I#am#here

运行示例代码

zip

在这里插入图片描述
cppreference

#include <ranges>
std::string s1 = "abcd";
std::vector<int> v = {0,1,2,3,5};
fmt::print("{}\n", std::views::zip(s1,v));
// (a,0) (b,1) (c,2) (d,3)
std::array a {0,1};
std::string s2 = "AB";
std::string s3 = "-+";
fmt::print("{}\n", std::views::zip(a,s2,s3));
// (0,A,-) (1,B,+)
// using an explicit view object:
std::ranges::zip_view fv {s1,v};
fmt::print("{}\n", fv);
// (a,0) (b,1) (c,2) (d,3)

运行示例代码

zip_transform

在这里插入图片描述
cppreference

#include <ranges>
std::string s = "abcd";
std::vector<int> v = {0,1,2,3,4,5};
auto const f = [] (char c, int x) { return c + std::to_string(x); };
for (auto x : std::views::zip_transform(f,s,v)) { fmt::print("{} ",x); }
// ["a0","b1","c2","d3"]
// using an explicit view object:
std::ranges::zip_transform_view av {f,s,v};
for (auto x : av) { fmt::print("{} ",x); }
// ["a0","b1","c2","d3"]

运行示例代码

cartesian_product

在这里插入图片描述
cppreference

#include <ranges>
std::vector a {'a','b'};
std::vector b {1,2,3,4};
std::string c = "♥♣♦";
for (auto p : std::views::cartesian_product(a,b,c)) { fmt::print("{} ",p); }
/* [a,1,♥] [a,1,♣] [a,1,♦] [a,2,♥] [a,2,♣] [a,2,♦] [a,3,♥] [a,3,♣] [a,3,♦] [a,4,♥] [a,4,♣] [a,4,♦] [b,1,♥] [b,1,♣] [b,1,♦] [b,2,♥] [b,2,♣] [b,2,♦] [b,3,♥] [b,3,♣] [b,3,♦] [b,4,♥] [b,4,♣] [b,4,♦] */

运行示例代码

助手

subrange

在这里插入图片描述

cppreference
将一个迭代器和一个哨兵(或两个迭代器)结合成一个视图。

#include <ranges>
std::vector<int> v {9,1,3,7,4,5,3,8};
auto const srv = std::ranges::subrange(v.begin()+2, v.begin()+6);
for (int x : srv) { std::cout << x << ' '; }
// 3 7 4 5
for (int x : std::views::reverse(srv)) { std::cout << x << ' '; }
// 5 4 7 3

运行示例代码

enumerate

在这里插入图片描述
cppreference
enumerate 在最新的 g++/libstdc++ 中可能尚不可用

#include <ranges>
std::string s = "ABCD";
for (auto const& [index, value] : std::views::enumerate(s)) {
  std::cout << "@" << index << ":" << value << '\n';
}
// @0:A @1:B @2:C @3:D
std::vector<std::string> words = {"This", "is", "a", "test."};
for (auto const& [index, value] : std::views::enumerate(words)) {
  std::cout << "@" << index << ":" << value << '\n';
}
// @0:This @1:is @2:a @3:test.

运行示例代码

元组投影

从一系列元组中提取出一系列单个元素。

keys

在这里插入图片描述
cppreference

#include <ranges>
std::vector<std::pair<char,int>> v {{'b',3},{'a',4},{'z',2},{'k',9}};
fmt::print("{}\n", std::views::keys(v)); // [b,a,z,k]
// using pipeline notation:
fmt::print("{}\n", v | std::views::keys); // [b,a,z,k]

印象示例代码

values

在这里插入图片描述
cppreference

#include <ranges>
std::vector<std::pair<char,int>> v {{'b',3},{'a',4},{'z',2},{'k',9}};
fmt::print("{}\n", std::views::values(v)); // [3,4,2,9]
// using pipeline notation:
fmt::print("{}\n", v | std::views::values); // [3,4,2,9]

运行示例代码

elements

在这里插入图片描述
cppreference

#include <ranges>
std::vector<std::tuple<char,int,std::string>> v {
  {'b',3,"hearts"}, {'a',4,"clubs"}, {'z',2,"spades"}, {'k',9,"diamonds"} };
fmt::print("{}\n", std::views::elements<2>(v));
// hearts clubs spades diamonds
// using pipeline notation:
fmt::print("{}\n", v | std::views::elements<2>);
// hearts clubs spades diamonds

运行示例代码

非范围–>范围

从非范围输入生成范围的工厂

iota

在这里插入图片描述
cppreference

#include <ranges>
auto i37 = std::views::iota(3,8);
for (int x : i37) { std::cout << x << ' '; }
// 3 4 5 6 7
auto i17 = std::views::iota(1) | std::views::take(7);
for (int x : i17) { std::cout << x << ' '; }
// 1 2 3 4 5 6 7

运行示例代码

repeat

在这里插入图片描述
cppreference

#include <ranges>
auto const r51 = std::views::repeat(1,5);
for (int x : r51) { std::cout << x << ' '; }
// 1 1 1 1 1
auto const r4a = std::views::repeat('a') | std::views::take(4);
for (char x : r4a) { std::cout << x << ' '; }
// a a a a

运行示例代码

istream

在这里插入图片描述
cppreference

empty

在这里插入图片描述
cppreference

single

在这里插入图片描述
cppreference

特殊适配器

ref_view

在这里插入图片描述
cppreference

owning_view

在这里插入图片描述
cppreference

all

在这里插入图片描述
cppreference

common

在这里插入图片描述
cppreference

备忘单

在这里插入图片描述

相关内容

Standard Sequence Views
cppreference: Algorithms Library
cppreference: Containers Library
An Overview of Standard Ranges  (Tristan Brindle, 2019)
Conquering C++20 Ranges  (Tristan Brindle, 2021)
Range Algorithms, Views and Actions - A Comprehensive Guide  (Yitzchaki Dvir, 2019)
From STL to Ranges: Using Ranges Effectively  (Garland Jeff, 2019)
What a View! Building Your Own (Lazy) Range Adaptors [1/2]  (Di Bella Chris, 2019)
What a View! Building Your Own (Lazy) Range Adaptors [2/2]  (Di Bella Chris, 2019)
Using C++20 Ranges Effectively  (Jeff Garland, 2019)

附上原文链接

如果文章对您有用,请随手点个赞,谢谢!^_^

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值