C++20 中最优雅的那个小特性 - Ranges

C++20 中最优雅的那个小特性 - Ranges

大家好,今天我们来聊聊 C++20 的一项非常重要的新特性——Ranges,可以让你的代码更优雅、更高效、更炫酷,如果你是一个对代码有所追求的小伙伴,那么这个特性你绝对值得拥有!
c++20-Ranges

啥是 Ranges和std::views ?

首先,我们来说说什么是 Range。简单来说,Range 就是一种可以遍历的序列。你可以把它想象成更智能、更灵活的数组或者容器。C++20 引入了 Ranges 这个概念,让我们可以更方便地操作这些序列。

再来说说 std::views,这其实是 C++20 里提供的一系列工具函数,用来对序列进行各种变换。std::views 可以帮我们过滤、转换、拼接等等,让我们以一种非常直观的方式对序列进行操作。

Range和std::views啥关系?

其实,Range和std::views就像是给你一盘水果(Range),然后你拿着各种刀子和工具(std::views)把这些水果处理成你想要的样子。

  • Range:数据的集合。
  • std::views:处理数据的各种工具,比如过滤、变换、切片等等。

举个例子

假如我们有一个数组,我们想要过滤出其中的偶数,然后再把这些偶数加倍。这在以前需要写挺多代码,但有了 Ranges 和 std::views 后,变得非常简单明了:

#include <iostream>
#include <vector>
#include <ranges>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 * 2; });for (int n : result) {std::cout << n << ' ';}return 0;
}

通过这段代码,我们可以看到,std::views::filter 和 std::views::transform 让我们能一步步地对序列进行处理,代码不仅简洁,而且非常直观,整个过程编码体验真叫一个舒畅啊。

Ranges 与函数式编程

接下来聊聊 Ranges 是怎么有助于函数式编程的。函数式编程的一个核心理念是通过一系列函数的组合来处理数据,而不是通过改变变量的状态。Ranges 恰好和这个理念非常契合。

首先,Ranges 提供了惰性计算的特性,这意味着序列的变换操作在真正需要用到的时候才会执行,不会提前执行。这样做不仅效率高,而且能避免很多不必要的计算。其次,Ranges 让我们可以很自然地把一连串操作用管道的方式串起来。这种方式让代码看起来像流水线一样,每一步都在变换数据。而且每一步的操作都是独立的函数,没有副作用,这和函数式编程的“纯函数”理念非常吻合。

以下将详细探讨 Ranges 和函数式编程之间的关系,以及如何在 C++20 中利用这些新特性来写出更具函数式风格的代码。

1. 声明式代码

在函数式编程中,开发者通常使用声明式代码来描述“做什么”,而不是“怎么做”。C++20 的 Ranges 库通过视图适配器和算法,使代码更具声明性。

示例

传统迭代器代码:

#include <vector>
#include <iostream>void traditional_example(const std::vector<int>& numbers) {std::vector<int> result;for (auto n : numbers) {if (n % 2 == 0) {result.push_back(n * n);}}for (auto n : result) {std::cout << n << ' ';}std::cout << std::endl;
}

使用 Ranges 的声明式代码:

#include <vector>
#include <iostream>
#include <ranges>void ranges_example(const std::vector<int>& numbers) {auto result = numbers| std::views::filter([](int n) { return n % 2 == 0; })| std::views::transform([](int n) { return n * n; });for (auto n : result) {std::cout << n << ' ';}std::cout << std::endl;
}

通过 Ranges,代码变得更简洁、更具声明性,且易读性和维护性大大提高。

2. 惰性求值

函数式编程的一个重要概念是惰性求值(Lazy Evaluation)。惰性求值只有在需要时才进行计算,避免不必要的计算和内存消耗。

示例

C++20 的 Ranges 通过视图实现了惰性求值:

#include <iostream>
#include <ranges>int main() {auto numbers = std::views::iota(1, 1000000) | std::views::filter([](int n) { return n % 2 == 0; })| std::views::transform([](int n) { return n * n; })| std::views::take(5); // 仅获取前5个结果for (auto n : numbers) {std::cout << n << ' ';}std::cout << std::endl;return 0;
}

在这个例子中,将 iota 生成的一个范围进行筛选、转换和截取,仅在实际使用时才执行这些操作,实现了惰性求值。

4 16 36 64 100 ...Program finished with exit code 0
Press ENTER to exit console.

3. 高阶函数

函数式编程中,高阶函数是指可以接受函数作为参数或返回函数的函数。在 C++20 的 Ranges 库中,视图适配器和算法本质上可以看作高阶函数。

示例

#include <vector>
#include <iostream>
#include <ranges>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6};auto even_filter = [](int n) { return n % 2 == 0; };auto square_transform = [](int n) { return n * n; };auto result = numbers | std::views::filter(even_filter)| std::views::transform(square_transform);for (auto n : result) {std::cout << n << ' ';}std::cout << std::endl;return 0;
}

在这个例子中,filtertransform 高阶函数接受筛选和转换的函数,并将它们应用于范围上。

4 16 36 ...Program finished with exit code 0
Press ENTER to exit console.

4. 不可变性

函数式编程强调数据的不可变性(Immutability)。虽然 C++ 默认是一个可变性较强的语言,但在使用 Ranges 时,通过惰性求值和组合操作,我们可以更接近数据的不可变性。

示例

传统方式:

#include <vector>
#include <algorithm>
#include <iostream>void traditional_example(std::vector<int>& numbers) {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; });for (auto n : result) {std::cout << n << ' ';}std::cout << std::endl;
}

使用 Ranges:

#include <vector>
#include <ranges>
#include <iostream>void ranges_example(const std::vector<int>& numbers) {auto result = numbers| std::views::filter([](int n) { return n % 2 == 0; })| std::views::transform([](int n) { return n * n; });for (auto n : result) {std::cout << n << ' ';}std::cout << std::endl;
}

在 Ranges 示例中,输入的 numbers 是不可变的。我们创建了一个新的视图,而不是改变原始数据。

5. 管道操作

函数式编程中一个流行的概念是管道(Pipeline)。它允许将一系列操作按顺序组合起来,形成一个数据处理流水线。

示例

通过 | 管道操作符,Ranges 实现了函数式编程中的管道概念:

#include <iostream>
#include <vector>
#include <ranges>int main() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6};auto result = numbers| std::views::filter([](int n) { return n % 2 == 0; })| std::views::transform([](int n) { return n * n; });for (auto n : result) {std::cout << n << ' ';}std::cout << std::endl;return 0;
}

这种方式使得代码语义更加清晰,数据流的处理步骤一目了然。讲到这儿,估计很多朋友会好奇了,这个 | 运算符是怎么做到这般神奇的?这就得说到运算符重载了。C++20 的 Pipeline 运算符 | 通过运算符重载,实现了高效、简洁的链式操作。这非常有助于函数式编程,让代码简洁易读,同时让数据变换变得更加纯粹、透明。

标准库支持的视图

C++20 Ranges 库提供了多种视图,可以满足各种序列操作需求,比如:

  • std::views::iota:生成一个从某个值开始的无限序列。
  • std::views::take:获取视图中的前几个元素。
  • std::views::drop:跳过视图中的前几个元素。
  • std::views::split:根据特定的分隔符将序列分割成多个子序列。
  • std::views::reverse:将序列逆序输出。
  • std::views::join:将嵌套范围扁平化。

再来一个扁平化的例子

#include <iostream>
#include <ranges>
#include <vector>int main() {std::vector<std::vector<int>> nested = { {1, 2}, {3, 4}, {5, 6} };auto flat_view = nested | std::views::join;for (auto n : flat_view) {std::cout << n << ' ';}std::cout << std::endl;
}
1 2 3 4 5 6 ...Program finished with exit code 0
Press ENTER to exit console.

结语

quotes-Evolution-is-necessary-to-meet-the-challenges

C++20 的 Ranges 库为 C++ 提供了许多函数式编程的特性,使得代码更加声明性、易读、易维护。通过 Ranges 和 std::views,开发者可以利用高阶函数、惰性求值、管道操作等函数式编程概念来处理数据流和集合,编写出更高效和优雅的代码。

这些特性不仅增强了 C++ 的表达能力,也让开发者能够以更简洁和自然的方式来进行复杂的数据操作和变换。希望上述内容能帮助你理解和应用 C++20 的 Ranges 库,更好地结合函数式编程理念进行开发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/470496.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python多进程间通讯(包含共享内存方式)

文章目录 1 通过非共享内存配合队列方式2 通过共享内存配合队列方式 注&#xff1a;本博文测试环境为Linux系统。 1 通过非共享内存配合队列方式 下面是一个常见的生产者与消费者的模式示例&#xff0c;这里分别启动了两个子进程&#xff0c;一个为生产者&#xff08;producer…

深入理解接口测试:实用指南与最佳实践5.0(一)

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;.NET、Java.测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/cat…

2024.11.12_大数据的诞生以及解决的问题

大数据的诞生以及解决的问题 视频一&#xff1a;大数据诞生的背景原因&#xff1a;传统的数据处理架构无法满足海量的数据存储和计算需求 视频三&#xff1a;区分离线处理场景和实时处理场景视频五&#xff1a;传统的大数据与现代的大数据区别&#xff08;离线场景&#xff09;…

ML 系列: 第 24 节 — 离散概率分布(泊松分布)

目录 一、说明 二、固定时间间隔示例 三、固定间隔的示例 四、泊松分布的主要特征 五、示例 5.1 平均客户数的计算&#xff1a; 5.2 用于计算和绘制泊松分布的 Python 代码&#xff1a; 一、说明 泊松概率分布是一种离散概率分布&#xff0c;它表示在固定的时间或空间间隔内发生…

闯关leetcode——3174. Clear Digits

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/clear-digits/description/ 内容 You are given a string s. Your task is to remove all digits by doing this operation repeatedly: Delete the first digit and the closest non-digit cha…

机器情绪及抑郁症算法

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;编程探索专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年11月12日17点02分 点击开启你的论文编程之旅https://www.aspiringcode.com/content?id17230869054974 计算机来理解你的情绪&a…

【深圳大学】数据结构A+攻略(计软版)

1. 考试 1.1 形式 分为平时&#xff0c;笔试&#xff0c;机试三部分。其中&#xff1a; 平时占30%&#xff0c;包含平时OJ测验和课堂练习&#xff0c;注意这个可能会因老师的不同和课题组的新策略而改变。笔试占60%&#xff0c;是分值占比的主要部分。机试占10%。 1.2 题型…

Chrome使用IE内核

Chrome使用IE内核 1.下载扩展程序IE Tab 2.将下载好的IE Tab扩展程序拖拽到扩展程序界面&#xff0c;之后重启chrome浏览器即可

使用pytest+openpyxl做接口自动化遇到的问题

最近使用pytestopenpyxl做了个接口自动化的小项目&#xff0c;遇到了一些问题。 首先&#xff0c;使用pytest这个框架&#xff0c;主要是使用了pytest.fixture, pytest.mark.parametrize这两个fixture去做参数化&#xff0c;里面注入的数据是用openpyxl来实现的。 接口介绍&a…

IEC60870-5-104 协议源码架构详细分析

IEC60870-5-104 协议源码架构 前言一、资源三、目录层级一二、目录层级二config/lib60870_config.hdependencies/READMEexamplesCMakeLists.txtcs101_master_balancedcs104_client_asyncmulti_client_servertls_clienttls_server说明 make这些文件的作用是否需要导入这些文件&a…

TensorRT基础知识

github:https://github.com/NVIDIA/TensorRT 官网快速入门链接&#xff1a;Quick Start Guide :: NVIDIA Deep Learning TensorRT Documentation 引言&#xff1a; TensorRT 是 NVIDIA 推出的一个高性能深度学习推理库&#xff0c;专门用于优化和加速已经训练好的深度学习模型…

jenkins提交gitee后自动部署

jenkins中安装gitee插件 Gitee Plugin​​​​​​ 配置gitee WebHook 生成giteeHook密码 去gitee中配置webHook 输入jenkins中的url和生成的密码 当我们再提交后就可以自动部署 gitee官方配置

软件测试面试八股文(超详细整理)

请你说一说测试用例的边界 参考回答&#xff1a; 边界值分析法就是对输入或输出的边界值进行测试的一种黑盒测试方法。通常边界值分析法是作为对等价类划分法的补充&#xff0c;这种情况下&#xff0c;其测试用例来自等价类的边界。 常见的边界值 1)对16-bit 的整数而言 32…

【金融风控】特征评估与筛选详解

内容介绍 掌握单特征分析的衡量指标 知道 IV&#xff0c;PSI等指标含义 知道多特征筛选的常用方法 掌握Boruta,VIF,RFE,L1等特征筛选的使用方法 【理解】单特征分析 什么是好特征 从几个角度衡量&#xff1a;覆盖度&#xff0c;区分度&#xff0c;相关性&#xff0c;稳定…

链游系统定制化开发:引领游戏产业的新时代

在数字革命的浪潮中&#xff0c;链游&#xff08;区块链游戏&#xff09;作为一种新兴游戏形式&#xff0c;正重新定义游戏产业的发展方向。链游将区块链技术与传统游戏结合&#xff0c;使游戏体验更加公平透明&#xff0c;并赋予玩家真正的资产所有权。这一领域不仅为玩家带来…

2024 年 8 个最佳 API 设计工具图文介绍

8 个最佳 API 设计工具推荐&#xff0c;包括 Apifox、Postman、Swagger、Insomnia、Stoplight、Hoppscotch、RapidAPI和Paw。 详细介绍&#xff1a;2024 年 8 个最佳 API 设计工具推荐

知识库管理系统:企业数字化转型的加速器

在数字化转型的大潮中&#xff0c;知识库管理系统&#xff08;KBMS&#xff09;已成为企业提升效率和创新能力的关键工具。本文将探讨知识库管理系统的定义、企业建立知识库的必要性&#xff0c;以及如何快速搭建企业知识库。 知识库管理系统是什么&#xff1f; 知识库管理系统…

24/11/12 算法笔记<强化学习> Policy Gradient策略梯度

gradient的核心就是每次更新前要重新收集&#xff0c;每个阶段的actor是不一样的. 策略梯度算法的核心思想&#xff1a; 策略表示&#xff1a;首先&#xff0c;策略梯度方法需要一个策略&#xff0c;该策略能够根据当前的状态选择一个动作。这个策略通常由一个参数化的函数表示…

物理设备命名规则(Linux网络服务器 15)

Linux系统中的一切都是文件&#xff0c;硬件设备也不例外。既然都是文件&#xff0c;就必须有文件名称。系统内核中udev设备管理器会自动把硬件名称规范化起来&#xff0c;目的是让用户通过设备文件的名字可以大致了解设备属性以及分区信息。这对于陌生的设备来说特别方便。另外…

SciPy:Python 科学计算工具包的全面教程

SciPy&#xff1a;Python 科学计算工具包的全面教程 引言 在数据科学和科学计算的领域&#xff0c;Python 已经成为一种流行的编程语言。作为 Python 的核心库之一&#xff0c;SciPy 提供了高效的数值计算功能&#xff0c;是科学计算、工程和数学应用中不可或缺的工具。本文将…