《C++ Primer》第十章 泛型算法

news2024/11/14 13:40:22

《C++ Primer》第十章 泛型算法

10.1 概述

大多数算法定义在头文件algorithm中,还有一些算法在numeric中。例如标准库算法find:

int val = 42;//即将查找的值
//如果在vec中找到想要的元素,则返回结果指向它,否则返回vec.cend()
auto result = find(vec.cbegin(), vec.end(), val);

string val = "a value";//我们要查找的值
//此调用在list中查找string元素
auto result=find(lst.cbegin(),lse.cend(), val);

//由于指针就像内置数组上的迭代器一样,因此可以用find在数组中查找值
int ia[] = {27, 210, 12 47, 109, 83};
int val = 83;
int* result = find(begin(ia), end(ia), val);
//在ia[1]、ia[2]和ia[3]中查找给定元素
auto result = find(ia+1,ia+4,val);

10.2 初识泛型算法

只读算法:find()、count()、accumulate()

accumulate(): 定义在numeric中,接受三个参数,前两个指出了需要求和的元素的范围,第三个参数是和的初值。例如:

//对vec中的元素求和,和的初值是0
int sum = accumulate(vec.cbegin(), vec.cend(), 0);

//通过调用accumulate将vector中所有string元素连接起来
string num = accumulate(v.cbegin(), v.cend(), string(""));
//将空串当做一个字符串字面值传递给第三个参数是不可以的,会导致一个编译错误
//错误:const char* 上没有定义+运算符
string sum = accumulate(v.cbegin(), v.cend(), "");

只读算法equal(): 用于确定两个序列是否保存相同的值。它将第一个序列中的每个元素和第二个序列中的对应元素进行比较。如果所有对应元素都相等,则返回true, 否则返回false。此算法接受三个迭代器:前两个表示第一个序列中的元素范围,第三个表示第二个序列的首元素。

//roster2中的元素数目应该至少与roster1一样多
equal(roster1.cbegin(), roster1.cend(), roster2.cbegin());

那些只接受一个单一迭代器来表示第二个序列的算法,都假定第二个序列至少与第一个序列一样长。

写容器元素的算法:fill接受一对迭代器表示一个范围,还接受一个值作为第三个参数。fill将给定的这个值赋予输入序列中的每个元素。

fill(vec.begin(), vec.end(), 0);//将每个元素重置为0
//将容器中的一个子序列设置为10
fill(vec.begin(), vec.begin() + vec.size()/2, 10);

算法不检查写操作:

fill_n接受一个单迭代器、一个计数器和一个值。它将给定值赋予迭代器指向的元素开始的指定个元素。

vector<int> vec;
//使用vec, 赋予不同值
fill_n(vec.begin(), vec.size(), 0);//将所有元素重置为0
fill_n(dest, n ,val)
fill_n假定dest指向一个元素,而从dest开始的序列至少包含n个元素

vector<int> vec;//容量
//灾难:修改vec中的10个(不存在)的元素
fill_n(vec.begin(), 10, 0);

back_inserter:接受一个指向容器的引用,返回一个与该容器绑定的插入迭代器。当我们通过此迭代器赋值时,赋值运算符会调用push_back将一个具有给定值的元素添加到容器中。

vector<int> vec;//空向量
auto it = back_inserter(vec);//通过它赋值会将元素添加到vec中
*it = 42;//vec现在有一个元素,值为42
//使用back_inserter来创建一个迭代器,作为算法的目的位置来使用
//正确:back_inserter创建一个插入迭代器,可以用来向vec添加元素
fill_n(back_inserter(vec), 10 , 0);

拷贝算法:copy可实现内置数组的拷贝

int a1[] = {0,1,2,3,4,5,6,7,8,9};
int a2[sizeof(a1)/sizeof(*a1)];//a2和a1大小一样
//ret指向拷贝到a2的尾元素之后的位置
auto ret = copy(begin(a1), end(a1), a2);//把a1的内容拷贝给a2

算法replace:读入一个序列,并将其中所有给定值的元素都改为另一个值。接受4个参数:前两个是迭代器,表示输入序列,后两个一个是要搜索的值,另一个是新值。它将所有等于第一个值的元素替换为第二个值。

//将所有值为0的元素改为42
replace(ilist.begin(), ilist.end(), 0, 42);
//保持原序列不变,使用back_inserter按需要增长目标序列
replace_copy(ilst.cbegin(), ilist.cend(),
			back_inserter(ivec), 0, 42);

重排容器元素的算法:

消除重复单词:

void elimDups(vector<string> &words)
{
	//按字典序排序words,以便查找重复单词
	sort(words.begin(), words.end());
	//unique重排输入范围,使得每个单词只出现一次
	//排列在范围的前部, 返回指向不重复区域之后一个位置的迭代器
	auto end_unique = unique(words.begin(), words.end());
	//使用向量操作erase删除重复单词
	words.erase(end_unique, words.end());
}

10.3定制操作

谓词:返回结果是一个能用作条件的值。分为一元谓词和二元谓词。一元、二元代表着接受几个参数。示例如下:

//比较函数,用来按长度排序单词
bool isShorter(const string &s1, const string &s2)
{
	return s1.size()<s2.size();
}
//按长度由短至长排序words
sort(words.begin(), words.end(), isShorter);

lambda表达式:表示一个可调用的代码单元。可以将其理解为一个未命名的内联函数。与任何函数类似,一个lambda具有一个返回类型、一个参数列表和一个函数体。表达式形式如下:

[capture list] (parameter list) -> return type { function body }

其中,capture list(捕获列表)是一个lambda所在函数中定义的局部变量的列表(通常为空):return type、parameter list 和 function body 与任何普通函数一样,分别表示返回类型、参数列表和函数体。但是,lambda必须使用尾置返回。

isShorter函数的lambda:

[](const string &a, const string &b){
	return a.size()<b.size();
}
//使用lambda来调用stable_sort
stable_sort(words.begin(), words.end(),
            [](const string &a, const string &b)
            {return a.size() < b.size();});

使用捕获列表:

[sz](const string &a)
	{ return a.size() >= sz; }

调用find_if

//获取一个迭代器,指向第一个满足size()>=sz的元素
auto wc = find_if(words.begin(),  words.end(),
	[sz](const string &a)
		{ return a.size() >= sz; });]
//计算满足size>=sz的元素的数目
auto count = words.end() - wc;
cout << count <<" "<<make_plural(count, "word", "s")
    << " of length "<< sz << " or longer"<<endl;

for_each算法: 接受一个可调用对象,并对输入序列中每个元素调用此对象。

//打印长度大于等于给定值的单词,每个单词后面接一个空格
for_each(wc, words.end(),
		[](const string &s){cout<<s<<" "});
cout<<endl;

lambda 捕获和返回:lambda的数据成员在lambda对象创建时被初始化

值捕获:类似参数传递,变量的捕获方式也可以是值或引用。与传值参数类似,采用值捕获的前提是变量可以拷贝。与参数不同,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝:

void fcn1()
{
	size_t v1 = 42;//局部变量
	//将v1拷贝到名为f的可调用对象
	auto f = [v1]{ return v1; };
	v1 = 0;
	auto j = f();//j为42;f保存了我们创建它时的拷贝
}

引用捕获:

void fcn2()
{
	size_t v1 = 42;//局部变量
	//对象f2包含v1的引用
	auto f2 = [&v1]{ return v1; };
	v1 = 0;
	auto j = f2();//j为0;f2保存v1的引用,而非拷贝
}

希望biggies函数接受一个ostream的引用,用来输出数据,并接受一个字符作为分隔符

void biggies(vector<string> &words, vector<string>::size_type sz, ostream &os = cout, 
				char c = ' ')
{
	...
	//打印count的语句改为打印os
	for_each(words.begin(), words.end(),
			[&os, c](const string &s) { os<<s<<c; });
}

隐式捕获:为了指示编译器推断捕获列表,应在捕获列表中写一个&或=。&告诉编译器采用捕获引用方式, =则表示采用值捕获方式。

//sz为隐式捕获,值捕获方式
wc = find_if(words.begin(), words.end(),
			[=](const string &s)
				{ return s.size()>=sz; });

可以混合使用隐式捕获和显示捕获:

void biggies(vector<string> &words,	
				vector<string>:: size_type sz,
				ostream &os = cout, char c=' ')
{
	//os隐式捕获,引用捕获方式;c显示捕获,值捕获方式
	for_each(words.begin(), words.end(),
				[&, c](const string &s) { os<<s<<c; });
	for_each(words.begin(),words.end(),
				[=,&os](const string &s) { os<<s<<c; });
}

在这里插入图片描述

可变lambda:默认情况下,对于一个值被拷贝的变量,lambda不会改变其值。可以在参数列表首加上mutable来改变被捕获的变量值。:

void fcn3()
{
	size_t v1 = 42;
	//f可以改变它所捕获的变量的值
	auto f = [v1] () mutable { return +v1; };
	v1 = 0;
	auto j = f();//j为43
}

一个引用捕获的变量是否可以修改依赖于此引用指向的是一个const 类型还是一个非const类型。

void fcn4()
{
	size_t v1 = 42;
	//v1是一个非const变量的引用
    //可以通过f2的引用来改变它
	auto f2 = [&v1] { return ++v1; };
	v1 = 0;
	auto j = f2();//j为1
}

指定lambda返回类型:当我们需要为一个lambda定义返回类型时,必须使用尾置返回类型

transform(vi.begin(), vi.end(), vi.begin(),
			[](int i)->int
			{ if(i<0) return -i; else return i; });

参数绑定:在find_if调用中的lambda比较一个string 和一个给定大小。但不能用这个函数作为find_if的一个参数。因为find_if接受一元谓词,因此传递给find_if的可调用对象必须接受单一参数。

bool check_size(const string &s, string::size_type sz)
{
	return s.size()>=sz;
}

标准库bind函数:将bind函数看作一个通用的函数适配器,它接受一个可调用对象。生成一个新的可调用对象来“适应”原对象的参数列表:

auto newCallable = bind(callable, arg_list);

绑定check_size的sz参数:

//check6是一个可调用对象,接受一个string类型的参数
//并用此string和值6来调用check_size
auto check6 = bind(check_size, _1, 6);

string s = "hello";
bool b1 = check6(s);//check6(s)会调用check_size(s,6);

//使用bind,可以将原来基于lambda的find_if调用
auto wc = find_if(words.begin(), words.end(), [sz](const string &a));

//替换为check_size版本
auto wc = find_if(words.begin(), words.end(),bind(check_size, _1, size));

使用placeholders名字:如_1对应的using声明为:

using std:: placeholders::1;

可以另一种不同形式的using语句,希望namespace_name的名字都可以可以直接使用:

using namespace namespace_name;

用bind重排参数顺序:

//按单词长度由短至长排序
sort(words.begin(), words.end(), isShorter);
//按单词长度由长至短排序
sort(words.begin(), words.end(), bind(isShorter, _2, _1));

绑定引用参数:与lambda相似,有时对有些绑定的参数希望以引用方式传递,或是要绑定参数的 类型无法拷贝。例如,为了替换一个引用方式捕获ostream的lambda:

//os是一个局部变量,引用是一个输出流
//c是一个局部变量,类型为char
for_each(words.begin(),words.end(),[&os, c](const string &s){ os<<s<<c; });
//可以很容易编写一个函数,完成相同的工作
ostream &print(ostream &os, const string &s, char c)
{
    return os<<s<<c;
}

但是不能直接用bind来代替对os的捕获:

//错误:不能拷贝os
for_each(words.begin(), words.end(), bind(print,os,_1,' '));

原因在于bind拷贝其参数,而我们不能拷贝一个ostream。如果希望传递给bind一个对象而又不拷贝它,就必须使用标准库ref函数:

for_each(words.begin(),words.end(),bind(print, ref(os), _1,' '));

函数ref返回一个对象,包含给定的引用,此对象是可以拷贝的。标准库中还有一个cref函数,生成一个保存const引用的类。

10.4 再探迭代器

在这里插入图片描述

在这里插入图片描述

插入迭代器:被绑定到一个容器上,用来向容器插入元素。

在这里插入图片描述

back_inserter: 调用push_back,总是插入到容器尾元素之后

front_inserter:调用push_front,总是插入到容器元素之前

inserter: 调用insert,总是插入到给定位置

list<int> lst = {1,2,3,4};
list<int> lst2,lst3;//空lst
//拷贝完成后,lst2包括4,3,2,1
copy(lst.cbegin(), lst.cend(), front_inserter(lst2));
//拷贝完成后,lst3包括1,2,3,4
copy(lst.cbegin(), lst.cend(),inserter(lst3, lst3.begin()));

流迭代器:被绑定到输入或输出流上,可用来遍历所有关联的IO流

iostream迭代器:istream_iterator读取输入流,ostream_iterator向一个输出流写数据。

在这里插入图片描述
在这里插入图片描述

反向迭代器:向后而不是向前移动。除了forward_list之外的标准库容器都有反向迭代器。

反向迭代器需要递减运算符。

移动迭代器:不是拷贝其中的元素,而是移动它们。

10.5 泛型算法结构

在这里插入图片描述

算法形式参数:

在这里插入图片描述

10.6 特定容器算法

在这里插入图片描述

在这里插入图片描述
splice成员:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小样本学习--学习记录

之前在做课题的时候&#xff0c;把数据不均衡和小样本的概念混淆了&#xff0c;昨天看了一篇论文&#xff1a;《 面向小样本数据的机器学习方法研究综述 &#xff08;陈良臣&#xff0c;傅德印&#xff09;》 &#xff0c;这篇论文写的非常清晰。推荐阅读。 网上的一些综述整理…

2月VR大数据:硬件份额变化不大,PS VR2首发游戏超50款

Hello大家好&#xff0c;每月一期的VR内容/硬件大数据统计又和大家见面了。 想了解VR软硬件行情么&#xff1f;关注这里就对了。我们会统计Steam平台的用户及内容等数据&#xff0c;每月初准时为你推送&#xff0c;不要错过喔&#xff01;本数据报告包含&#xff1a;Steam VR硬…

擅长捉弄的内存马同学:Valve内存马

前言 内存马的文章已经很久没有更新过了&#xff0c; 这篇文章不太适合想直接学习利用Valve内存马的师傅 &#xff0c;因为我这篇文章可能会有大篇笔墨去说Tomcat容器&#xff0c;至于原因就是我想更深入的了解一些Tomcat&#xff0c;而Valve内存马属于已经被师傅们玩烂了的一…

现场设备发生故障,如何第一时间通知相关人员?

一、前言 虹科物联网HMI作为一站式物联网解决方案&#xff0c;致力于解决用户在数据采集和可视化、远程监控、边缘计算、软PLC、数据存储&#xff08;SQL数据库和CSV文件&#xff09;、数据上云&#xff08;OPC UA、MQTT&#xff09;等方面的需求&#xff0c;帮助企业快速实现…

运营数据分析模型—用户画像

用户画像 伴随着大数据应用的讨论、创新,个性化技术成为了一个重要落地点。相比传统的线下会员管理、问卷调查、购物篮分析,大数据第一次使得企业能够通过互联网便利地获取用户更为广泛的反馈信息,为进一步精准、快速地分析用户行为习惯、消费习惯等重要商业信息,提供了足…

SpringCloud系列(十四)[分布式搜索引擎篇] - 索引库及文档的增删改查操作

本文主要介绍一下索引库及文档的一些增删改查操作, 以下都是一些常用的操作, 无需死记硬背, 只需要用到的时候常翻阅即可;   当然学习索引库和文档的一些基本操作还是要先在虚拟机启动一下 elasticsearch 及 kibana, 启动成功后输入 172.16.xx.xxx:5601 后出现以下界面即启动成…

信创-东方通和达梦适配

1 TLQ8.0 简单的例子&#xff0c;发送MQ&#xff0c;然后收消息连接是一样的&#xff0c;要不断去拉取数据消费的public static void main(String[] args) {//发送消息的目的队列String queName "lq";//连接工厂类QueueConnectionFactory queueConnectionFactory n…

制造业仓库很乱,该如何有效的管理呢?

首先来统计一下制造业仓库很乱的问题&#xff1a; 1.管理多仓库/多店铺&#xff0c;库存不清2.库存控制不合理&#xff0c;出现滞销与脱销的情况3.库存盘点时间长&#xff0c;数据不准确在传统管理模式下&#xff0c;一些中小型制造业的仓库&#xff0c;物料摆放位置、明细等都…

TOOLS_Seaborn相关性可视化分析示例

TOOLS_Seaborn相关性可视化分析示例 相关性应用场景 如要回答 推测 网站留存、观看时长、收藏次数、转发次数、关注数之间是否存在相关性&#xff0c;以及相关性有多大的问题&#xff1b; 对于更有关注的留存&#xff0c;可以尝试分析一个与其相关性比较大的因素&#xff0c…

【大数据专题】大数据理论基础01之分布式CPA原理深入理解

分布式CAP定理 CAP定理又称CAP原则&#xff0c;指的是在一个分布式系统中&#xff0c;Consistency&#xff08;一致性&#xff09;、 Availability&#xff08;可用性&#xff09;、Partition tolerance&#xff08;分区容错性&#xff09;&#xff0c;最多只能同时三个特性中…

编写一个Vue插件,上传NPM官网开源使用

插件开发及上传NPM流程 完成组件封装、组件完成统一封装成插件插件入口文件配置src/main.js 中引入口文件、安装插件配置 package.json 文件npm run lib 打包umd.min.js 配置到 package.json登录 NPM 上传插件npm i 安装引入使用 一、编写好组件 二、插件入口文件配置 plugins/…

更安全更稳定,阿里云斩获多项云系统稳定安全运行优秀案例

近日&#xff0c;阿里云凭借在稳定性领域的全栈投入&#xff0c;获评中国信通院混沌工程实验室 2022 年度杰出贡献企业&#xff0c;并斩获“云系统稳定安全运行优秀案例”活动中多领域优秀案例。阿里云持续推动企业 IT 系统建设&#xff0c;保障千行百业安全稳定的实现数字化转…

普通人如何用AI帮你干活——娱乐1

案例描述&#xff1a;一天朋友突然发信息跟我说&#xff1a;看你朋友全最近总在显摆AI作画&#xff0c;看起来好像有点牛逼&#xff1b;我家小朋友最近在参加一个国家绘画比赛&#xff0c;能不能让你的AI帮忙画几张有趣的有创意的画。给小朋友做灵感启发&#xff0c;小朋友可以…

如何处理负面评论?利用负面评论发挥优势

每家公司都应该做的一件事&#xff1a;回复评论&#xff01; 37%的买家积极考虑对评论的回应&#xff0c;以评估和对品牌的看法。所以不要忘记回复评论&#xff01; 如何处理负面评论 如果您的公司正在经历大量负面评论&#xff0c;请了解您的产品团队如何利用它们来发挥自己的…

CSS隐藏元素、BFC、元素居中、布局

1、css中有哪些方式可以隐藏页面元素&#xff1f;区别是什么&#xff1f;1.1 display:none元素在页面上将彻底消失&#xff0c;元素本身占有的空间会被其他元素占有&#xff0c;导致浏览器的重排和重绘。特点&#xff1a;元素不可见&#xff0c;不占据空间&#xff0c;不会触发…

面试阿里自动化测试工程师被狂虐,回家猛补3个月,成功上岸字节

前言 大家好&#xff0c;我是小祖&#xff0c;个人背景&#xff1a;985中下游&#xff0c;无大厂实习&#xff08;小厂打杂&#xff09;&#xff0c;无竞赛&#xff0c;无相关论文。 毕业几年&#xff0c;表面上用过很多技术&#xff0c;但都没能深入学习&#xff0c;一年的工…

python 支付宝营销活动现金红包开发接入流程-含接口调用加签

1 创建网页/移动应用 2 配置接口加签方式 涉及到金额的需要上传证书&#xff0c;在上传页面有教程&#xff0c; 在支付宝开放平台秘钥工具中生成CSR证书&#xff0c;会自动保存应用公钥和私钥到电脑上&#xff0c;调用支付宝接口需要应用私钥进行加签 上传完CSR证书后会有三个…

互联网衰退期,测试工程师35岁的路该怎么走...

国内的互联网行业发展较快&#xff0c;所以造成了技术研发类员工工作强度比较大&#xff0c;同时技术的快速更新又需要员工不断的学习新的技术。因此淘汰率也比较高&#xff0c;超过35岁的基层研发类员工&#xff0c;往往因为家庭原因、身体原因&#xff0c;比较难以跟得上工作…

DSP_TMS320F28335_PIE学习笔记

前言 本文重点探讨DSP PIE模块的学习笔记&#xff0c;由于学这部内容的时候&#xff0c;是用28335学的&#xff0c;所以标题是用的28335&#xff0c;但其实28377D和28335的PIE使用基本上是一样的&#xff0c;也是可以借鉴的。 正文 原理 讲点原理&#xff0c;PIE&#xff0…

linux minio更改密码MINIO_ACCESS_KEY报错

minio版本RELEASE.2020-11-13T20-10-18Z启动文件配置如下cat run.sh#!/bin/bashexport MINIO_ACCESS_KEYminioexport MINIO_SECRET_KEYfasffnohup /opt/minio/minio.RELEASE.2020-11-13T20-10-18Z server http://192.168.100.x/data/minio_data http://192.168.100.x/data/mini…