当前位置: 首页 > news >正文

C++学习:六个月从基础到就业——C++20:范围(Ranges)基础

C++学习:六个月从基础到就业——C++20:范围(Ranges)基础

本文是我C++学习之旅系列的第五十一篇技术文章,也是第三阶段"现代C++特性"的第十三篇,介绍C++20引入的范围(Ranges)库的基础知识。查看完整系列目录了解更多内容。

引言

STL算法和容器是C++编程中最强大的工具之一,但传统的STL算法接口存在一些使用上的不便:需要显式传递迭代器对、难以组合多个算法操作、代码可读性不佳等。C++20引入的范围(Ranges)库重新设计了算法接口,引入了视图(Views)的概念,并提供了方便的管道操作符,显著改善了这些问题。

想象一下,如果你想获取一个集合中所有偶数的平方,传统STL算法需要这样写:

std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::vector<int> result;// 筛选偶数
std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(result),[](int n) { return n % 2 == 0; });// 计算平方
std::transform(result.begin(), result.end(),result.begin(),[](int n) { return n * n; });

而使用Ranges库,代码变得简洁明了:

std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};auto result = numbers| std::views::filter([](int n) { return n % 2 == 0; })| std::views::transform([](int n) { return n * n; });// 使用结果
for (int n : result) {std::cout << n << " ";  // 输出: 4 16 36 64 100
}

本文将介绍C++20 Ranges库的基础知识,包括其核心概念、范围算法、视图(Views)和管道操作,帮助你理解和应用这一强大的新特性。

目录

  • C++20:范围(Ranges)基础
    • 引言
    • 目录
    • 范围库概述
      • 传统STL算法的局限
      • 范围库的设计理念
      • 核心组件
    • 范围概念
      • 什么是范围
      • 范围类别和需求
      • 迭代器与哨兵
    • 范围算法
      • 算法改进
      • 常用范围算法
      • 投影参数
    • 视图(Views)基础
      • 视图的概念
      • 基本视图类型
      • 视图与容器的区别
    • 管道操作
      • 管道语法
      • 视图组合
      • 惰性求值
    • 实际应用示例
      • 数据过滤与转换
      • 字符串处理
      • 数值计算
    • 总结

范围库概述

传统STL算法的局限

传统STL算法虽然功能强大,但存在几个明显的使用不便:

  1. 迭代器对耦合:必须同时提供起始和结束迭代器
std::vector<int> v = {1, 2, 3, 4, 5};
std::sort(v.begin(), v.end());  // 必须提供两个迭代器
  1. 错误风险:容易传递不匹配的迭代器对
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = {4, 5, 6};
// 潜在风险:传递了不匹配的迭代器对
std::sort(v1.begin(), v2.end());  // 未定义行为
  1. 组合算法困难:算法间组合需要中间容器,代码冗长
std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> temp;
std::vector<int> result;// 筛选
std::copy_if(source.begin(), source.end(), std::back_inserter(temp),[](int n) { return n > 2; });// 转换
std::transform(temp.begin(), temp.end(),std::back_inserter(result),[](int n) { return n * n; });
  1. 返回值不便:许多算法返回迭代器,需要检查合法性
auto it = std::find(v.begin(), v.end(), 42);
if (it != v.end()) {  // 必须检查是否有效// 使用*it
} else {// 未找到
}

范围库的设计理念

C++20范围库基于几个核心理念设计:

  1. 范围作为统一概念:将容器或迭代器对视为一个整体的"范围"
std::vector<int> v = {1, 2, 3, 4, 5};
// 将容器作为范围直接传递
std::ranges::sort(v);  
  1. 组合操作:通过管道操作符(|)组合多个操作
auto result = container| std::views::filter(pred)| std::views::transform(func);
  1. 惰性求值:视图不立即执行操作,而是在需要结果时才计算
// 创建视图只是定义操作,不执行实际计算
auto even_squares = numbers| std::views::filter([](int n) { return n % 2 == 0; })| std::views::transform([](int n) { return n * n; });// 只有遍历时才实际执行操作
for (int n : even_squares) {// 在这里才真正执行过滤和转换std::cout << n << " ";
}
  1. 更清晰的语义:代码更具声明性,意图更加明确
// 传统方式
std::vector<int> result;
std::copy_if(v.begin(), v.end(), std::back_inserter(result), [](int n) { return n > 0; });// Ranges方式
auto positive = v | std::views::filter([](int n) { return n > 0; });

核心组件

范围库包含四个主要组件:

  1. 范围概念(Range Concepts):定义何为范围的约束
template<typename T>
concept range = requires(T& t) {std::ranges::begin(t);std::ranges::end(t);
};
  1. 范围算法(Range Algorithms):接受范围作为参数的算法
// 算法直接接受范围参数
std::ranges::sort(container);
std::ranges::find(container, value);
  1. 视图(Views):轻量级、不修改原始数据的范围适配器
// 基本视图
std::views::all      // 整个范围的视图
std::views::filter   // 根据谓词筛选元素
std::views::transform // 转换元素
std::views::take     // 取前N个元素
std::views::drop     // 跳过前N个元素
  1. 范围适配器(Range Adaptors):修改范围属性的工具
// 范围适配器
std::views::reverse  // 反转范围
std::views::join     // 连接嵌套范围
std::views::split    // 分割范围

范围概念

什么是范围

在C++20中,范围(Range)是一个抽象概念,表示元素序列。更具体地说,任何可以通过调用std::ranges::begin()std::ranges::end()得到有效迭代器对的类型都是范围。

#include <ranges>
#include <vector>
#include <list>
#include <string>
#include <iostream>void demonstrate_ranges() {// 这些都是范围std::vector<int> vec = {1, 2, 3, 4, 5};        // 向量是范围std::list<double> lst = {1.1, 2.2, 3.3};       // 列表是范围std::string str = "Hello";                     // 字符串是范围int arr[] = {10, 20, 30, 40, 50};              // 数组是范围// 普通指针对也可以是范围const char* cstr = "C-string";auto ptr_range = std::ranges::subrange(cstr, cstr + 8);// 视图也是范围auto even = vec | std::views::filter([](int n) { return n % 2 == 0; });// 使用范围变量的示例std::cout << "Vector elements:";for (int n : vec) std::cout << " " << n;std::cout << std::endl;std::cout << "Even numbers:";for (int n : even) std::cout << " " << n;std::cout << std::endl;
}

范围的关键特性:

  1. 提供了表示元素序列的统一抽象
  2. 支持对整个序列而非迭代器对进行操作
  3. 可以是有限的(如容器)或无限的(如生成器)
  4. 可以是普通容器、视图或迭代器对

范围类别和需求

C++20定义了多种范围概念,根据底层迭代器的能力形成层次结构:

#include <ranges>
#include <vector>
#include <list>
#include <forward_list>
#include <iostream>template<typename R>
void print_range_capabilities() {std::cout << "- range: " << std::ranges::range<R> << std::endl;std::cout << "- input_range: " << std::ranges::input_range<R> << std::endl;std::cout << "- forward_range: " << std::ranges::forward_range<R> << std::endl;std::cout << "- bidirectional_range: " << std::ranges::bidirectional_range<R> << std::endl;std::cout << "- random_access_range: " << std::ranges::random_access_range<R> << std::endl;std::cout << "- contiguous_range: " << std::ranges::contiguous_range<R> << std::endl;std::cout << "- sized_range: " << std::ranges::sized_range<R> << std::endl;std::cout << "- view: " << std::ranges::view<R> << std::endl;
}int main() {std::cout << "std::vector capabilities:" << std::endl;print_range_capabilities<std::vector<int>>();std::cout << "\nstd::list capabilities:" << std::endl;print_range_capabilities<std::list<int>>();std::cout << "\nstd::forward_list capabilities:" << std::endl;print_range_capabilities<std::forward_list<int>>();std::cout << "\nFilter view capabilities:" << std::endl;print_range_capabilities<decltype(std::vector<int>{} | std::views::filter([](int) { return true; }))>();return 0;
}

主要范围类别:

  1. std::ranges::range:基本范围概念,支持begin()end()
  2. std::ranges::input_range:可以读取元素的范围
  3. std::ranges::forward_range:可以多次遍历的范围
  4. std::ranges::bidirectional_range:可以双向遍历的范围
  5. std::ranges::random_access_range:支持随机访问的范围
  6. std::ranges::contiguous_range:内存连续存储的范围
  7. std::ranges::sized_range:可以常数时间获取大小的范围
  8. std::ranges::view:轻量级、非拥有元素的范围

不同容器支持不同级别的范围能力:

  • std::vector 支持所有范围能力(除了view
  • std::list 是双向范围但不支持随机访问
  • std::forward_list 是前向范围但不支持双向遍历
  • 视图(如filter view)通常继承底层范围的能力,但始终是view

迭代器与哨兵

C++20范围库引入了"哨兵"(Sentinel)的概念,允许迭代器和终止条件类型不同:

#include <ranges>
#include <vector>
#include <string>
#include <iostream>int main() {// 传统方式:begin和end类型相同std::vector<int> v = {1, 2, 3, 4, 5};auto it_begin = v.begin();  // std::vector<int>::iteratorauto it_end = v.end();      // 同样类型// 范围库:允许不同类型的终止条件std::string str = "Hello, world!";// 自定义视图:到null字符为止auto null_terminated = std::ranges::subrange(str.c_str(),  // const char*std::unreachable_sentinel  // 特殊哨兵类型);// 计算长度(不包括null终止符)std::cout << "String length: " << std::ranges::distance(null_terminated) << std::endl;// 查找特定字符的视图auto until_comma = std::ranges::subrange(str.begin(),std::ranges::find(str, ',')  // 迭代器指向逗号);std::cout << "Text before comma: ";for (char c : until_comma) {std::cout << c;}std::cout << std::endl;return 0;
}

哨兵概念的优势:

  1. 更灵活的范围表示:终止条件可以是谓词而非具体位置
  2. 无限序列支持:可以表示无限序列,只在需要时检查终止条件
  3. 懒惰计算:哨兵可以推迟终止条件的计算
  4. 优化机会:编译器可以针对特定哨兵类型优化代码

范围算法

算法改进

C++20为标准库算法提供了对应的范围版本,带来几个主要改进:

  1. 直接接受范围参数,而非迭代器对
#include <ranges>
#include <algorithm>
#include <vector>
#include <iostream>int main() {std::vector<int> numbers = {5, 3, 1, 4, 2};// 传统STL算法std::sort(numbers.begin(), numbers.end());// 范围版本算法std::ranges::sort(numbers);  // 更简洁return 0;
}
  1. 返回更有意义的结果
#include <ranges>
#include <algorithm>
#include <vector>
#include <iostream>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5};// 传统STL查找auto it = std::find(numbers.begin(), numbers.end(), 3);if (it != numbers.end()) {std::cout << "Found: " << *it << std::endl;}// 范围版本查找auto it_ranges = std::ranges::find(numbers, 3);if (it_ranges != numbers.end()) {std::cout << "Found with ranges: " << *it_ranges << std::endl;}// 更复杂的例子:找出最大和最小值auto [min_it, max_it] = std::ranges::minmax_element(numbers);std::cout << "Min: " << *min_it << ", Max: " << *max_it << std::endl;return 0;
}
  1. 支持投影(Projections):在应用算法前变换元素
#include <ranges>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>struct Person {std::string name;int age;
};int main() {std::vector<Person> people = {{"Alice", 30},{"Bob", 25},{"Charlie", 35}};// 使用投影按年龄排序std::ranges::sort(people, {}, &Person::age);// 输出排序后的结果for (const auto& person : people) {std::cout << person.name << ": " << person.age << std::endl;}// 查找年龄最大的人auto oldest = std::ranges::max_element(people, {}, &Person::age);if (oldest != people.end()) {std::cout << "Oldest person: " << oldest->name << " (" << oldest->age << ")" << std::endl;}return 0;
}
  1. 概念约束:算法明确指定了对输入类型的要求
// std::ranges::sort的简化定义
template<std::random_access_range R, typename Comp = std::ranges::less, typename Proj = std::identity>
requires std::sortable<std::ranges::iterator_t<R>, Comp, Proj>
constexpr auto sort(R&& r, Comp comp = {}, Proj proj = {}) -> ...;

常用范围算法

C++20范围库包含了标准库中大部分算法的范围版本:

#include <ranges>
#include <algorithm>
#include <numeric>
#include <vector>
#include <iostream>void demonstrate_range_algorithms() {std::vector<int> numbers = {5, 2, 8, 1, 9, 3, 7, 4, 6};std::vector<int> dest(numbers.size());// 排序算法std::ranges::sort(numbers);std::cout << "排序后: ";for (int n : numbers) std::cout << n << " ";std::cout << std::endl;// 查找算法auto it = std::ranges::find(numbers, 7);if (it != numbers.end()) {std::cout << "找到7,位置: " << std::distance(numbers.begin(), it) << std::endl;}// 计数算法int count = std::ranges::count_if(numbers, [](int n) { return n % 2 == 0; });std::cout << "偶数个数: " << count << std::endl;// 复制算法std::ranges::copy_if(numbers, dest.begin(), [](int n) { return n > 5; });std::cout << "大于5的数: ";for (int i = 0; i < std::ranges::count(numbers, true, [](int n) { return n > 5; }); ++i) {std::cout << dest[i] << " ";}std::cout << std::endl;// 变换算法std::ranges::transform(numbers, dest.begin(), [](int n) { return n * n; });std::cout << "平方值: ";for (int i = 0; i < numbers.size(); ++i) {std::cout << dest[i] << " ";}std::cout << std::endl;// 其他常用算法auto [min, max] = std::ranges::minmax(numbers);std::cout << "最小值: " << min << ", 最大值: " << max << std::endl;std::ranges::reverse(numbers);std::cout << "反转后: ";for (int n : numbers) std::cout << n << " ";std::cout << std::endl;bool all_positive = std::ranges::all_of(numbers, [](int n) { return n > 0; });std::cout << "全部为正: " << (all_positive ? "是" : "否") << std::endl;
}

常见范围算法分类:

  1. 非修改序列算法ranges::find, ranges::count, ranges::all_of
  2. 修改序列算法ranges::copy, ranges::transform, ranges::replace
  3. 排序和相关算法ranges::sort, ranges::partial_sort, ranges::nth_element
  4. 数值算法ranges::accumulate(注意:部分数值算法尚未有范围版本)
  5. 集合算法ranges::set_union, ranges::set_intersection

投影参数

范围算法的一个重要特性是支持投影(Projections),允许在实际应用算法前转换元素:

#include <ranges>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>struct Student {std::string name;std::vector<int> scores;// 计算平均分double average() const {if (scores.empty()) return 0;int sum = 0;for (int score : scores) sum += score;return static_cast<double>(sum) / scores.size();}
};int main() {std::vector<Student> students = {{"Alice", {85, 90, 82}},{"Bob", {76, 88, 95}},{"Charlie", {90, 92, 98}},{"David", {65, 75, 80}}};// 使用投影基于平均分排序std::ranges::sort(students, {}, [](const Student& s) { return s.average(); });// 显示结果std::cout << "学生按平均分排序:" << std::endl;for (const auto& student : students) {std::cout << student.name << ": " << student.average() << std::endl;}// 找出平均分最高的学生auto top_student = std::ranges::max_element(students, {}, [](const Student& s) { return s.average(); });std::cout << "\n平均分最高的学生: " << top_student->name << " (" << top_student->average() << ")" << std::endl;// 找出有满分(100)的学生auto perfect_student = std::ranges::find_if(students, [](const std::vector<int>& scores) {return std::ranges::any_of(scores, [](int score) { return score == 100; });},&Student::scores  // 投影:获取学生的分数向量);if (perfect_student != students.end()) {std::cout << "\n有满分的学生: " << perfect_student->name << std::endl;} else {std::cout << "\n没有学生获得满分" << std::endl;}return 0;
}

投影的优势:

  1. 代码简洁性:无需创建单独的比较器函数
  2. 语义清晰:明确表达对哪些属性进行操作
  3. 可组合性:可以与其他算法参数(如比较器)组合
  4. 易于维护:当数据结构变化时,只需更改投影函数

视图(Views)基础

视图的概念

视图(View)是范围库中的核心概念,表示一个轻量级、非拥有元素的范围:

#include <ranges>
#include <vector>
#include <iostream>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 创建视图auto even_numbers = numbers | std::views::filter([](int n) { return n % 2 == 0; });// 视图不修改原始数据std::cout << "原始数据:";for (int n : numbers) std::cout << " " << n;std::cout << std::endl;std::cout << "视图内容:";for (int n : even_numbers) std::cout << " " << n;std::cout << std::endl;// 修改原始数据会影响视图numbers[0] = 0;  // 将1改为0std::cout << "修改后视图:";for (int n : even_numbers) std::cout << " " << n;std::cout << std::endl;return 0;
}

视图的关键特性:

  1. 轻量级:创建视图通常是O(1)操作,不涉及元素复制
  2. 惰性求值:只在遍历时才执行操作
  3. 非拥有:视图不拥有元素,只引用原始范围
  4. 可组合:可以通过管道操作符组合多个视图

基本视图类型

C++20提供了多种预定义视图,位于std::views命名空间:

#include <ranges>
#include <vector>
#include <string>
#include <iostream>void demonstrate_basic_views() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// all:整个范围的视图auto all_view = std::views::all(numbers);// filter:过滤元素auto even = numbers | std::views::filter([](int n) { return n % 2 == 0; });// transform:变换元素auto squares = numbers | std::views::transform([](int n) { return n * n; });// take:取前N个元素auto first_five = numbers | std::views::take(5);// drop:丢弃前N个元素auto skip_five = numbers | std::views::drop(5);// reverse:反转范围auto reversed = numbers | std::views::reverse;// 输出各种视图std::cout << "原始数据:";for (int n : all_view) std::cout << " " << n;std::cout << std::endl;std::cout << "偶数:";for (int n : even) std::cout << " " << n;std::cout << std::endl;std::cout << "平方值:";for (int n : squares) std::cout << " " << n;std::cout << std::endl;std::cout << "前5个:";for (int n : first_five) std::cout << " " << n;std::cout << std::endl;std::cout << "跳过前5个:";for (int n : skip_five) std::cout << " " << n;std::cout << std::endl;std::cout << "反转:";for (int n : reversed) std::cout << " " << n;std::cout << std::endl;// iota:生成整数序列auto sequence = std::views::iota(1, 6);  // 1到5std::cout << "整数序列:";for (int n : sequence) std::cout << " " << n;std::cout << std::endl;// 字符串相关视图std::string text = "Hello,World,C++,Ranges";auto words = text | std::views::split(',');std::cout << "分割字符串:" << std::endl;for (auto word : words) {for (char c : word) std::cout << c;std::cout << std::endl;}
}

常用预定义视图:

  1. std::views::all:引用整个范围
  2. std::views::filter:根据谓词筛选元素
  3. std::views::transform:变换每个元素
  4. std::views::take:取前N个元素
  5. std::views::take_while:取满足条件的前缀元素
  6. std::views::drop:丢弃前N个元素
  7. std::views::drop_while:丢弃满足条件的前缀元素
  8. std::views::reverse:反转范围
  9. std::views::elements:获取元组元素
  10. std::views::keys/std::views::values:获取键值对的键或值
  11. std::views::iota:生成连续递增的整数序列
  12. std::views::split:按分隔符分割范围
  13. std::views::join:连接嵌套范围

视图与容器的区别

视图和容器有几个关键区别:

#include <ranges>
#include <vector>
#include <string>
#include <iostream>
#include <chrono>void compare_view_and_container() {std::vector<int> numbers(1'000'000);// 填充数据for (int i = 0; i < numbers.size(); ++i) {numbers[i] = i;}// 计时创建副本auto start = std::chrono::high_resolution_clock::now();std::vector<int> filtered_container;for (int n : numbers) {if (n % 2 == 0) {filtered_container.push_back(n);}}auto mid = std::chrono::high_resolution_clock::now();// 创建视图auto filtered_view = numbers | std::views::filter([](int n) { return n % 2 == 0; });auto end = std::chrono::high_resolution_clock::now();// 计算时间auto container_time = std::chrono::duration_cast<std::chrono::milliseconds>(mid - start).count();auto view_time = std::chrono::duration_cast<std::chrono::milliseconds>(end - mid).count();std::cout << "创建容器副本时间: " << container_time << " ms" << std::endl;std::cout << "创建视图时间: " << view_time << " ms" << std::endl;// 内存使用对比std::cout << "容器元素数量: " << filtered_container.size() << std::endl;std::cout << "视图元素数量: " << std::ranges::distance(filtered_view) << std::endl;std::cout << "容器内存占用: " << filtered_container.size() * sizeof(int) << " 字节" << std::endl;std::cout << "视图理论内存占用: < 100 字节" << std::endl;// 修改原始数据影响numbers[0] = 1;  // 改为奇数// 容器不受影响std::cout << "修改原始数据后:" << std::endl;std::cout << "容器首元素: " << filtered_container[0] << std::endl;std::cout << "视图首元素: " << *filtered_view.begin() << std::endl;
}

主要区别:

  1. 内存所有权

    • 容器拥有并管理其元素的内存
    • 视图不拥有元素,只引用其他范围
  2. 创建成本

    • 创建容器副本需要复制元素,时间和空间成本与元素数成正比
    • 创建视图是O(1)操作,几乎没有开销
  3. 修改传播

    • 修改容器副本不影响原始数据
    • 修改原始数据会反映在引用它的视图中
  4. 惰性求值

    • 容器中的元素是预先计算的
    • 视图元素是按需计算的,可能多次计算
  5. 适用场景

    • 容器适用于需要存储和拥有数据的场景
    • 视图适用于临时转换和处理数据的场景

管道操作

管道语法

Ranges库引入了管道操作符(|),使数据处理更加直观:

#include <ranges>
#include <vector>
#include <iostream>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 使用管道语法auto result = numbers| std::views::filter([](int n) { return n % 2 == 0; })  // 偶数| std::views::transform([](int n) { return n * n; });   // 平方// 输出结果std::cout << "偶数的平方:";for (int n : result) std::cout << " " << n;std::cout << std::endl;// 管道操作符使用规则:// 1. 传统函数调用风格auto even_func = std::views::filter(numbers, [](int n) { return n % 2 == 0; });// 2. 管道风格 (通常更易读)auto even_pipe = numbers | std::views::filter([](int n) { return n % 2 == 0; });// 两种方式等效std::cout << "函数调用风格:";for (int n : even_func) std::cout << " " << n;std::cout << std::endl;std::cout << "管道风格:";for (int n : even_pipe) std::cout << " " << n;std::cout << std::endl;return 0;
}

管道语法的优势:

  1. 可读性:从左到右的数据流更符合人类思维习惯
  2. 组合性:轻松组合多个操作,无需中间变量
  3. 简洁性:减少样板代码
  4. 表达力:清晰表达数据转换意图

视图组合

Ranges库的强大之处在于可以轻松组合多个视图:

#include <ranges>
#include <vector>
#include <string>
#include <iostream>
#include <cctype>  // for toupperint main() {std::vector<std::string> words = {"apple", "banana", "cherry", "date", "elderberry", "fig", "grape", "honeydew"};// 组合多个视图auto processed = words| std::views::filter([](const std::string& s) { return s.length() > 5;  // 只要长词})| std::views::transform([](std::string s) {// 转换为大写for (char& c : s) c = std::toupper(c);return s;})| std::views::take(3);  // 只取前3个// 输出结果std::cout << "处理结果:" << std::endl;for (const auto& word : processed) {std::cout << word << std::endl;}// 更复杂的组合示例std::vector<std::vector<int>> nested = {{1, 2, 3},{4, 5, 6},{7, 8, 9}};auto flattened = nested| std::views::join  // 展平嵌套容器| std::views::filter([](int n) { return n % 2 != 0; })  // 奇数| std::views::transform([](int n) { return n * n; })    // 平方| std::views::reverse;  // 反转顺序std::cout << "\n展平处理:";for (int n : flattened) std::cout << " " << n;std::cout << std::endl;return 0;
}

视图组合的关键点:

  1. 顺序重要性:操作按照管道中指定的顺序执行
  2. 效率考虑:某些组合可能比其他组合更高效
  3. 视图延迟特性:无论多少操作组合,都只在遍历时执行
  4. 表达能力:复杂的数据转换可以简洁地表达

惰性求值

视图的一个核心特征是惰性求值,只在需要结果时才执行操作:

#include <ranges>
#include <vector>
#include <iostream>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 创建一个包含多个操作的视图std::cout << "创建视图..." << std::endl;auto result = numbers| std::views::filter([](int n) {std::cout << "过滤: " << n << std::endl;return n % 2 == 0;})| std::views::transform([](int n) {std::cout << "变换: " << n << std::endl;return n * n;});std::cout << "视图创建完成,尚未执行任何操作" << std::endl;// 获取首个元素std::cout << "\n获取第一个元素..." << std::endl;auto it = result.begin();std::cout << "第一个元素: " << *it << std::endl;// 获取下一个元素std::cout << "\n获取下一个元素..." << std::endl;++it;std::cout << "下一个元素: " << *it << std::endl;// 遍历剩余元素std::cout << "\n遍历剩余元素..." << std::endl;for (; it != result.end(); ++it) {std::cout << "元素: " << *it << std::endl;}// 重新遍历整个视图std::cout << "\n再次遍历(注意操作会重新执行)..." << std::endl;for (int n : result) {std::cout << "结果: " << n << std::endl;}return 0;
}

惰性求值的特点和优势:

  1. 按需计算:只计算实际需要的元素
  2. 节省内存:不需要存储中间结果
  3. 支持无限序列:可以处理理论上无限的数据流
  4. 避免不必要的工作:如果只需要前几个结果,不会处理所有元素
  5. 潜在缺点:多次遍历会重复计算,有时需要具体化(materialize)结果

实际应用示例

数据过滤与转换

Ranges库特别适合数据过滤和转换操作:

#include <ranges>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>struct Product {std::string name;double price;int stock;bool discontinued;
};void product_processing() {std::vector<Product> products = {{"Laptop", 1200.0, 5, false},{"Smartphone", 800.0, 12, false},{"Tablet", 400.0, 8, false},{"MP3 Player", 50.0, 0, true},{"Headphones", 120.0, 20, false},{"Camera", 600.0, 3, false},{"Printer", 250.0, 0, false},{"DVD Player", 80.0, 1, true}};// 查找可购买的产品(有库存且未停产)auto available = products | std::views::filter([](const Product& p) {return p.stock > 0 && !p.discontinued;});std::cout << "可购买的产品:" << std::endl;for (const auto& product : available) {std::cout << product.name << " - $" << product.price<< " (库存: " << product.stock << ")" << std::endl;}// 找出价格在一定范围内的产品auto mid_range = products| std::views::filter([](const Product& p) {return p.price >= 100 && p.price <= 500;})| std::views::transform([](const Product& p) {// 返回产品名称和价格return std::make_pair(p.name, p.price);});std::cout << "\n中等价位产品:" << std::endl;for (const auto& [name, price] : mid_range) {std::cout << name << " - $" << price << std::endl;}// 计算所有可用产品的总库存价值double total_value = 0.0;for (const auto& p : available) {total_value += p.price * p.stock;}std::cout << "\n总库存价值: $" << total_value << std::endl;// 按价格排序并显示前3名最贵的产品auto top_priced = products| std::views::filter([](const Product& p) { return p.stock > 0; })| std::ranges::to<std::vector>()  // 具体化为向量| std::views::transform([](const Product& p) {return std::make_pair(p.name, p.price);});// 注意:视图不保证保留原始顺序,需要排序std::vector<std::pair<std::string, double>> sorted_prices(top_priced.begin(), top_priced.end());std::ranges::sort(sorted_prices, std::ranges::greater{}, &std::pair<std::string, double>::second);std::cout << "\n价格最高的3个有库存产品:" << std::endl;for (const auto& [name, price] : sorted_prices | std::views::take(3)) {std::cout << name << " - $" << price << std::endl;}
}

字符串处理

Ranges库使字符串处理更加简洁优雅:

#include <ranges>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <cctype>void string_processing() {std::string text = "The quick brown fox jumps over the lazy dog";// 将字符串拆分为单词auto words = text | std::views::split(' ');std::cout << "单词列表:" << std::endl;for (auto word : words) {// 将视图转换为string以便打印std::string w(word.begin(), word.end());std::cout << w << std::endl;}// 找出所有长度大于3的单词auto long_words = text | std::views::split(' ')| std::views::filter([](auto word) {return std::ranges::distance(word) > 3;});std::cout << "\n长度大于3的单词:" << std::endl;for (auto word : long_words) {std::string w(word.begin(), word.end());std::cout << w << std::endl;}// 将每个单词的首字母大写std::string capitalized;bool new_word = true;for (char c : text) {if (new_word && std::isalpha(c)) {capitalized += std::toupper(c);new_word = false;} else {capitalized += c;if (c == ' ') new_word = true;}}std::cout << "\n首字母大写: " << capitalized << std::endl;// 计算文本中不同字母的出现频率(不区分大小写)std::array<int, 26> letter_counts = {0};for (char c : text | std::views::filter(::isalpha) | std::views::transform(::tolower)) {letter_counts[c - 'a']++;}std::cout << "\n字母频率:" << std::endl;for (int i = 0; i < 26; ++i) {if (letter_counts[i] > 0) {std::cout << static_cast<char>('a' + i) << ": " << letter_counts[i] << std::endl;}}
}

数值计算

Ranges库可以简化数值计算和数据分析:

#include <ranges>
#include <vector>
#include <iostream>
#include <numeric>
#include <cmath>
#include <iomanip>void numerical_calculations() {// 生成1到100的序列auto numbers = std::views::iota(1, 101);// 计算平均值double sum = std::accumulate(numbers.begin(), numbers.end(), 0.0);double mean = sum / std::ranges::distance(numbers);std::cout << "平均值: " << mean << std::endl;// 计算平方和double sum_squares = 0.0;for (int n : numbers) {sum_squares += n * n;}std::cout << "平方和: " << sum_squares << std::endl;// 生成斐波那契数列的前20个数std::vector<int> fibonacci;fibonacci.reserve(20);fibonacci.push_back(0);fibonacci.push_back(1);for (int i = 2; i < 20; ++i) {fibonacci.push_back(fibonacci[i-1] + fibonacci[i-2]);}std::cout << "\n斐波那契数列:";for (int n : fibonacci) std::cout << " " << n;std::cout << std::endl;// 查找小于1000的所有斐波那契数中的偶数auto even_fibs = fibonacci | std::views::filter([](int n) { return n < 1000 && n % 2 == 0; });std::cout << "小于1000的偶数斐波那契数:";for (int n : even_fibs) std::cout << " " << n;std::cout << std::endl;// 生成前10个质数std::vector<int> primes;for (int n = 2; primes.size() < 10; ++n) {bool is_prime = true;for (int i = 2; i <= std::sqrt(n); ++i) {if (n % i == 0) {is_prime = false;break;}}if (is_prime) primes.push_back(n);}std::cout << "\n前10个质数:";for (int p : primes) std::cout << " " << p;std::cout << std::endl;// 计算每个质数与下一个质数的差auto prime_gaps = primes| std::views::slide(2)  // C++23功能,在某些编译器可能不可用| std::views::transform([](auto pair) {return *std::next(pair.begin()) - *pair.begin();});// 如果slide不可用,可以使用替代方法std::vector<int> gaps;for (size_t i = 0; i < primes.size() - 1; ++i) {gaps.push_back(primes[i+1] - primes[i]);}std::cout << "质数间隔:";for (int gap : gaps) std::cout << " " << gap;std::cout << std::endl;
}

总结

C++20的范围(Ranges)库彻底改变了我们处理集合和算法的方式,为C++带来了更现代、更函数式的编程风格。主要优势包括:

  1. 更简洁的语法:直接对容器操作,无需显式传递迭代器对
  2. 更好的可读性:代码表达数据流向,更符合人类思维模式
  3. 组合能力:通过管道操作符轻松组合多个操作
  4. 惰性求值:按需执行操作,提高效率
  5. 视图抽象:轻量级引用原始数据,避免不必要的复制
  6. 投影参数:简化复杂数据类型的处理

范围库的基础概念、范围算法和基本视图类型为处理各种数据集合提供了强大的工具。通过管道操作和惰性求值,我们可以构建高效的数据处理流程,使代码更加简洁明了。

在下一篇文章中,我们将探讨Ranges库的更多高级特性,包括复杂视图组合、自定义视图、性能优化技巧以及与其他C++20特性的结合使用。


这是我C++学习之旅系列的第五十一篇技术文章。查看完整系列目录了解更多内容。

相关文章:

C++学习:六个月从基础到就业——C++20:范围(Ranges)基础

C学习&#xff1a;六个月从基础到就业——C20&#xff1a;范围(Ranges)基础 本文是我C学习之旅系列的第五十一篇技术文章&#xff0c;也是第三阶段"现代C特性"的第十三篇&#xff0c;介绍C20引入的范围(Ranges)库的基础知识。查看完整系列目录了解更多内容。 引言 S…...

【AGI】模型性能评估框架EvalScope

【AGI】模型性能评估框架EvalScope 项目地址&#xff1a;https://github.com/modelscope/evalscope ​ EvalScope 是由阿里巴巴魔搭社区&#xff08;ModelScope&#xff09;推出的一款开源模型评估框架&#xff0c;旨在为大语言模型&#xff08;LLM&#xff09;和多模态模型提供…...

【老马】离线版金融敏感信息加解密组件开源项目 encryption-local

前言 你是否存在这样的苦恼&#xff0c;数据需要安全存储&#xff0c;但是每个系统大家自己写&#xff0c;很浪费时间。。 每一个子项目各自为政&#xff0c;加解密搞得也无法统一。也许下面这个开源项目可以帮助你。 encryption-local 一个离线版本的金融敏感信息加解密工具…...

利用systemd启动部署在服务器上的web应用

0.背景 系统环境&#xff1a; Ubuntu 22.04 web应用情况&#xff1a; 前后端分类&#xff0c;前端采用react&#xff0c;后端采用fastapi 1.具体配置 1.1 前端配置 开发态运行&#xff08;启动命令是npm run dev&#xff09;,创建systemd服务文件 sudo nano /etc/systemd/…...

YOLOv5目标构建与损失计算

YOLOv5目标构建与损失计算 YOLOv5目标构建与损失计算构建目标关键步骤解析&#xff1a; 计算损失关键实现细节解析各损失分量说明 YOLOv5目标构建与损失计算 YOLOv5作为单阶段目标检测的经典算法&#xff0c;其高效的检测性能离不开精心设计的训练目标构建和损失计算策略。本文…...

【Linux】ELF与动静态库的“暗黑兵法”:程序是如何跑起来的?

目录 一、什么是库&#xff1f; 1. C标准库&#xff08;libc&#xff09; 2. C标准库&#xff08;libstdc&#xff09; 二、静态库 1. 静态库的生成 2. 静态库的使用 三、动态库 1. 动态库的生成 2. 动态库的使用 3. 库运行的搜索路径。 &#xff08;1&#xff09;原因…...

【图书管理系统】用户注册系统实现详解

引言 本系统允许用户输入用户名和密码&#xff0c;前端通过AJAX请求将数据发送到后端&#xff0c;后端验证并存储用户信息&#xff0c;同时为每个用户创建一个专属图书表。尽管这是一个基础实现&#xff0c;但它展示了前后端分离开发的核心思想。博客还将讨论潜在的优化点&…...

FastDFS分布式文件系统架构学习(一)

FastDFS分布式文件系统架构学习 1. FastDFS简介 FastDFS是一个开源的轻量级分布式文件系统&#xff0c;由淘宝资深架构师余庆设计并开发。它专为互联网应用量身定制&#xff0c;特别适合以中小文件&#xff08;如图片、文档、音视频等&#xff09;为载体的在线服务。FastDFS不…...

Oracle 内存优化

Oracle 的内存可以按照共享和私有的角度分为系统全局区和进程全局区&#xff0c;也就是 SGA和 PGA(process global area or private global area)。对于 SGA 区域内的内存来说&#xff0c;是共享的全局的。在 UNIX 上&#xff0c;必须为 Oracle 设置共享内存段(可以是一个或者多…...

算法题(149):矩阵消除游戏

审题&#xff1a; 本题需要我们找到消除矩阵行与列后可以获得的最大权值 思路&#xff1a; 方法一&#xff1a;贪心二进制枚举 这里的矩阵消除时&#xff0c;行与列的消除会互相影响&#xff0c;所以如果我们先统计所有行和列的总和&#xff0c;然后选择消除最大的那一行/列&am…...

AI:NLP 情感分析

💬 从零开始掌握情感分析:NLP 初学者实战指南 本文适合自然语言处理(NLP)入门者,聚焦于最热门应用之一——情感分析(Sentiment Analysis)。无论你是学生、工程师,还是数据爱好者,都可以通过本文了解情感分析的原理、方法和实现技巧。 🧠 一、什么是情感分析? 情感…...

LearnOpenGL---着色器

着色器的例子 文章目录 着色器的例子1.颜色变化的三角形2.构造三个顶点颜色不同的一个三角形 1.颜色变化的三角形 #include <glad/glad.h> #include <GLFW/glfw3.h>#include <iostream> #include <cmath>void framebuffer_size_callback(GLFWwindow* …...

计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 13.几何着色器(一)修改顶点

几何着色器 以下是OpenGL图像管线的主要阶段&#xff1a; 几何着色器&#xff08;Geometry Shader&#xff09; 几何着色器是OpenGL管线中的一个可选阶段&#xff0c;位于顶点着色器和片段着色器之间。它能够动态地生成或修改图元&#xff08;primitives&#xff09;。 主…...

如何利用 Java 爬虫获得某书笔记详情:实战指南

在知识分享和学习的领域&#xff0c;许多平台提供了丰富的书籍笔记和学习资源。通过 Java 爬虫技术&#xff0c;我们可以高效地获取这些笔记的详细信息&#xff0c;以便进行进一步的分析和整理。本文将详细介绍如何利用 Java 爬虫获取某书笔记详情&#xff0c;并提供完整的代码…...

【关联git本地仓库,上传项目到github】

目录 1.下载git2.绑定用户3.git本地与远程仓库交互4.github项目创建5.上传本地项目到github6.完结撒花❀❀❀&#xff01;&#xff01;&#xff01; 1.下载git git下载地址&#xff1a;https://git-scm.com/downloads 下载安装后创建快捷地址&#xff1a;&#xff08;此处比较…...

计算机科技笔记: 容错计算机设计05 n模冗余系统 TMR 三模冗余系统

NMR&#xff08;N-Modular Redundancy&#xff0c;N 模冗余&#xff09;是一种通用的容错设计架构&#xff0c;通过引入 N 个冗余模块&#xff08;N ≥ 3 且为奇数&#xff09;&#xff0c;并采用多数投票机制&#xff0c;来提升系统的容错能力与可靠性。单个模块如果可靠性小于…...

配置代理服务器访问github、google

配置代理服务器访问github、google 背景与原理配置环境配置步骤云主机配置Windows客户端创建SSH隧道安装 Windows 内置 OpenSSHssh config 配置文件创建动态代理隧道 浏览器代理设置 验证浏览器访问google、githubssh 访问github 背景与原理 由于网络政策限制&#xff0c;中国…...

Java API学习笔记

一.类 1. String 类 不可变性&#xff1a;String对象创建后不可修改&#xff0c;每次操作返回新对象 String str "Hello"; str.length(); str.charAt(0); str.substring(1, 4); str.indexOf("l"); str.equals("hel…...

C++ map容器: 插入操作

1. map插入操作基础 map是C STL中的关联容器&#xff0c;存储键值对(key-value pairs)。插入元素时有四种主要方式&#xff0c;各有特点&#xff1a; 1.1 头文件与声明 #include <map> using namespace std;map<int, string> mapStu; // 键为int&#xff0c;值…...

Linux SSH 远程连接全攻略:从加密原理到实战配置(含图解)

一、SSH 加密体系核心理论 &#xff08;一&#xff09;对称加密与非对称加密对比解析 1. 加密算法分类与应用场景 类型代表算法密钥数量加密速度安全性特点典型用途对称加密AES、3DES1 个★★★★☆密钥传输风险高会话数据加密非对称加密RSA、ECC2 个★★☆☆☆公钥可公开&a…...

项目制作流程

一、使用 CRA 创建项目 npx create-react-app name 二、按照业务规范整理项目目录 &#xff08;重点src目录&#xff09; 三、安装插件 npm install sass -Dnpm install antd --savenpm install react-router-dom 四、配置基础路由 Router 1. 安装路由包 react-router-dom …...

ctr查看镜像

# 拉取镜像到 k8s.io 命名空间 sudo nerdctl --namespace k8s.io pull nginx:1.23.4 # 验证镜像是否已下载 sudo nerdctl --namespace k8s.io images 下载镜像到k8s.io名称空间下 nerdctl --namespace k8s.io pull zookeeper:3.6.2 sudo ctr image pull --namespace k8s.io …...

【深度学习基础】从感知机到多层神经网络:模型原理、结构与计算过程全解析

【深度学习基础】从感知机到多层神经网络&#xff1a;模型原理、结构与计算过程全解析 1. 引言 神经网络的重要性&#xff1a; 作为人工智能的核心技术之一&#xff0c;神经网络通过模拟人脑神经元的工作机制&#xff0c;成为解决复杂模式识别、预测和决策任务的利器。从图像分…...

discuz X3.5批量新建用户

<?php define(IN_DISCUZ, true); require ./source/class/class_core.php; // 确保管理员权限&#xff08;可选&#xff0c;增加安全性&#xff09;删除这一段加粗则可以直接使用. if ($_G[adminid] ! 1) { exit(Access denied. Only admin allowed.); } C::app()->…...

symfonos: 1靶场

symfonos: 1 来自 <https://www.vulnhub.com/entry/symfonos-1,322/> 1&#xff0c;将两台虚拟机网络连接都改为NAT模式 2&#xff0c;攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.182&#xff0c;靶场IP192.168.23.252 3&…...

C# String 格式说明符

标准格式说明符数字格式说明符C 或 c&#xff1a;货币格式D 或 d&#xff1a;十进制数字格式E 或 e&#xff1a;科学计数法格式。F 或 f&#xff1a;固定点格式G 或 g&#xff1a;常规格式N 或 n&#xff1a;数字格式P 或 p&#xff1a;百分比格式X 或 x&#xff1a;十六进制格…...

Python高级特性深度解析:从熟练到精通的跃迁之路

Python高级特性深度解析&#xff1a;从熟练到精通的跃迁之路 引言 对于已经掌握Python基础语法的开发者而言&#xff0c;如何突破瓶颈进入高手行列&#xff1f;本文将从Python的高级特性入手&#xff0c;深入剖析那些能让代码更优雅、效率更高的技术点&#xff0c;助你完成从…...

【微信小程序 + 高德地图API 】键入关键字搜索地址,获取经纬度等

前言 又到熟悉的前言&#xff0c;接到个需求&#xff0c;要引入高德地图api&#xff0c;我就记录一下&#xff0c;要是有帮助记得点赞、收藏、关注&#x1f601;。 后续有时间会慢慢完善一些文章&#xff1a;&#xff08;画饼时间&#xff09; map组件自定义气泡、mark标记点…...

贪心、分治和回溯算法

1. 贪心算法 1.1. 贪心算法的概念 定义&#xff1a;在求解过程中&#xff0c;始终做出当前状态下看起来“最优”的选择&#xff0c;不回退。核心思想&#xff1a;每一步都选择当前最优解&#xff0c;期望最后得到全局最优解。 适用问题的特征&#xff1a; 问题可以分解成多个…...

window自带截图快捷键

Win Shift S&#xff1a;按此组合键后&#xff0c;会出现截图模式选择框&#xff0c;可选择矩形截图、任意形状截图、窗口截图和全屏幕截图&#xff0c;然后使用 “Ctrl V” 粘贴截图内容...

简单使用Slidev和PPTist

简单使用Slidev和PPTist 1 简介 前端PPT制作有很多优秀的工具包&#xff0c;例如&#xff1a;Slidev、revealjs、PPTist等&#xff0c;Slidev对Markdown格式支持较好&#xff0c;适合与大模型结合使用&#xff0c;选哟二次封装&#xff1b;revealjs适合做数据切换&#xff0c…...

1.2.2

某智慧养老平台的心率监测模块目前存在数据准确性不高、异常预警响应慢等问题&#xff0c;影响了老年人健康监测的体验和服务质量。作为人工智能训练师&#xff0c;你需要结合业务知识和人工智能技术&#xff0c;对该模块进行优化设计与实现。 &#xff08;1&#xff09;列出心…...

LeeCode 101.对称二叉树

给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 提示&#xff1a; 树中节点数目在范围 [1, 1000] 内-100 < Node.val < 100 进阶&#xff1a;你可以运用递归和迭代两种方法解决这个问题吗&#xff1f; 答案 && 测试代码&#xff1a; #include &…...

面向GIS的Android studio移动开发(二)--在地图上绘制电子围栏

电子围栏&#xff0c;校园跑的常客&#xff0c;也是定位打卡必不可少的东西 主要代码&#xff1a; 创建电子围栏代码 // 添加多边形地理围栏&#xff08;兼容2023版SDK&#xff09;private void addPolygon(String fenceName, List<LatLng> points) {if (points null…...

《从零开始:Spring Cloud Eureka 配置与服务注册全流程》​

关于Eureka的学习&#xff0c;主要学习如何搭建Eureka&#xff0c;将order-service和product-service都注册到Eureka。 1.为什么使用Eureka? 我在实现一个查询订单功能时&#xff0c;希望可以根据订单中productId去获取对应商品的详细信息&#xff0c;但是产品服务和订单服…...

能力验证及大练兵活动第一期

计算机 请根据计算机检材&#xff0c;回答以下问题&#xff1a; (10道题&#xff0c;共19.0分) 1. 计算机中曾挂载的Bitlocker加密分区的恢复密钥后6位为&#xff1f;&#xff08;答案格式&#xff1a;6位数字&#xff09; (1.0分) 答案&#xff1a;700755 2. 请写出曾远程连…...

TASK03【Datawhale 组队学习】搭建向量知识库

文章目录 向量及向量知识库词向量与向量向量数据库 数据处理数据清洗文档分割 搭建并使用向量数据库 向量及向量知识库 词向量与向量 词向量&#xff08;word embedding&#xff09;是一种以单词为单位将每个单词转化为实数向量的技术。词向量背后的主要想理念是相似或相关的…...

ProfibusDP转ModbusRTU的实用攻略

ProfibusDP转ModbusRTU的实用攻略 在工业自动化领域中&#xff0c;Profibus DP和Modbus RTU是两种常见的通信协议。 Profibus DP是一种广泛应用于过程控制和工厂自动化的现场总线标准&#xff0c;具有高实时性和可靠性。 而Modbus RTU则是一种串行通信协议&#xff0c;常用于…...

基于开源AI智能名片链动2+1模式S2B2C商城小程序源码的去中心化商业扩散研究

摘要&#xff1a;本文探讨在去中心化商业趋势下&#xff0c;开源AI智能名片链动21模式S2B2C商城小程序源码如何助力企业挖掘数据价值、打破信息孤岛&#xff0c;实现商业高效扩散。通过分析该技术组合的架构与功能&#xff0c;结合实际案例&#xff0c;揭示其在用户关系拓展、流…...

iOS 工厂模式

iOS 工厂模式 文章目录 iOS 工厂模式前言工厂模式简单工厂案例场景分析苹果类优点缺点 小结 工厂模式客户端调用**优点****缺点** 抽象工厂模式三个模式对比 前言 笔者之前学习了有关于设计模式的六大原则,之前简单了解过这个工厂模式,今天主要是重新学习一下这个模式,正式系统…...

LaTeX OCR - 数学公式识别系统

文章目录 一、关于 LaTeX OCR1、项目概览架构图2、相关链接资源3、功能特性 二、安装配置基础环境要求Linux 安装Mac 安装 三、使用指南1、快速训练&#xff08;小数据集&#xff09;2、完整训练&#xff08;大数据集&#xff09; 四、可视化功能训练过程可视化预测过程可视化 …...

Go 语言即时通讯系统开发日志-日志day2-5:架构设计与日志封装

Go语言即时通讯系统开发日志day2 计划&#xff1a;学习go中MySQL&#xff0c;Redis的使用&#xff0c;使用MySQL和Redis完成一个单聊demo。 总结&#xff1a;现在每天下午用来开发这个项目&#xff0c;如果有课的话可能学习时间只有3-4个小时&#xff0c;再加上今天的学习效率不…...

@JsonProperty和@JSONField 使用

JsonProperty和JSONField注解的区别 1.底层框架不同 JsonProperty 是Jackson实现的 JSONField 是fastjson实现的 2.用法不同 &#xff08;1&#xff09;bean序列化为Json&#xff1a; JsonProperty&#xff1a; ObjectMapper().writeValueAsString(Object value) JSONField&…...

从代码学习深度学习 - 近似训练 PyTorch版

文章目录 前言负采样 (Negative Sampling)层序Softmax (Hierarchical Softmax)代码示例总结前言 在自然语言处理(NLP)领域,词嵌入(Word Embeddings)技术如Word2Vec(包括Skip-gram和CBOW模型)已经成为一项基础且强大的工具。它们能够将词语映射到低维稠密向量空间,使得…...

代码上传gitte仓库

把代码push上去就行...

系统架构设计(十四):解释器风格

概念 解释器风格是一种将程序的每个语句逐条读取并解释执行的体系结构风格。程序在运行时不会先被编译为机器码&#xff0c;而是动态地由解释器分析并执行其语义。 典型应用&#xff1a;Python 解释器、JavaScript 引擎、Bash Shell、SQL 引擎。 组成结构 解释器风格系统的…...

掌握LINQ:查询语法与方法语法全解析

文章目录 引言1. 查询语法 vs 方法语法1.1 查询语法 (Query Syntax)1.2 方法语法 (Method Syntax)1.3 两种语法的比较 2. 基本的 LINQ 查询结构2.1 数据源2.2 查询操作2.3 查询执行 3. 查询表达式中的关键字3.1 基本关键字fromwhereselectorderbygroup byjoin 3.2 其他常用关键…...

Go 后端中双 token 的实现模板

下面是一个典型的 Go 后端双 Token 认证机制 实现模板&#xff0c;使用 Gin 框架 JWT Redis&#xff0c;结构清晰、可拓展&#xff0c;适合实战开发。 项目结构建议 /utils├── jwt.go // Access & Refresh token 的生成和解析├── claims.go // 从请求…...

GESP编程能力等级认证C++3级1-数组1

1 GESP编程能力等级认证C3级 1.1 GESP简介 GESP是CCF 编程能力等级认证的简称&#xff0c;它为青少年计算机和编程学习者提供学业能力验证的规则和平台。GESP 覆盖中小学阶段&#xff0c;符合年龄条件的青少年均可参加认证。 1.2 GESP的分级 C 编程测试划分为一至八级&…...

FreeRTOS “探究任务调度机制魅力”

引入 现如今随着单片机的资源越来越多&#xff0c;主频越来越高&#xff0c;在面临更复杂的功能实现以及对MCU性能的充分压榨&#xff0c;会RTOS已经成为一个必要的技能&#xff0c;新手刚开始学习的时候就很好奇“为什么代码可以放到两个循环里同时运行&#xff1f;”。接下来…...