第十章 泛型算法 362

10-1概述
标准库算法find
在这里插入图片描述传给find的前两个参数是表示元素范围的迭代器,第三个参数是要搜索的值
如果搜索成功,则返回第一个与给定值相等的迭代器
如果搜索失败,则返回find的第二个参数
算法如何工作
在这里插入图片描述
只要有一个迭代器可以用来访问元素,find就完全不依赖于容器类型(甚至无需理会保存元素的是不是容器)
迭代器令算法不依赖于容器,但算法依赖于元素类型的操作
10-2初始泛型算法
1、只读算法
一些算法只会读取输入范围中的元素,而不会改变其值
例如find、count、accumulate
accumulate定义在头文件numeric中,它接受三个参数,前两个参数指出了需要求和的元素的范围,第三个参数是和的初始值
算法和元素类型
用accumulate求和时,序列中元素类型必须与第三个参数匹配,或者能转化成第三个参数的类型
将vector中所有string元素连接起来:在这里插入图片描述
如果第三个参数是空串的字面值,会导致编译错误:
在这里插入图片描述
用于保存和的对象的类型是const char*,但const char*没有+运算符
操作两个序列的算法
只读算法equal,用于确定两个序列是否保存相同的值
它将第一个序列中的每一个元素与第二个元素中对应的元素比较,如果所有对应元素相等,则返回true,否则返回false
equal接受三个是迭代器的参数,前两个参数用来指出第一个序列元素的范围,第三个参数表示第二个序列的首元素
例如:
在这里插入图片描述
因为equal利用迭代器完成操作,所以两个容器可以不同,容器保存元素的类型也可以不同
第二个序列至少与第一个序列一样长
2、写容器元素的算法
算法不会改变容器的大小,必须确保序列足够大
算法fill接受一对迭代器表示一个范围,还接受一个值作为第三个参数,fill将给定的这个值赋给序列中的每个元素
在这里插入图片描述
算法不检查写操作
函数fill_n接受一个迭代器,一个计数值和一个值,它将给定值赋给迭代器指向的元素开始的指定个元素
在这里插入图片描述
fill_n假定写入指定个值是安全的
不要在空容器上调用fill_n
在这里插入图片描述
指定要写入10个元素,但容器是空的
介绍back_inserter
使用插入迭代器是一种保证算法有足够的元素空间来容纳输出数据
通常情况下,通过迭代器向容器元素赋值时,值被赋予迭代器指向的元素
通过插入迭代器赋值时,一个与赋值号右侧元素相等的值被添加到容器中
back_inserter函数定义在头文件iteratior中
back_inserter接受一个指向容器的引用,返回一个与该容器绑定的插入迭代器,当通过此迭代器赋值时,赋值运算符会调用pushback将给定值添加到容器中
在这里插入图片描述
在这里插入图片描述
拷贝算法
copy算法接受三个迭代器,前两个表示一个输入范围,打三个表示目的序列的起始位置
copy将输入范围中的元素拷贝到目的序列中
目的序列至少要包含与输入序列一样多的元素
例如:
在这里插入图片描述
copy返回的是其目的位置迭代器(递增后)的值,r2指向a2尾元素之后的位置
replace算法读入一个序列,并将其中所有给定值的元素都改为另一个值
replace接受四个参数,前两个是迭代器,表示输入序列,后两个一个是要搜索的值,另一个是新值
例如:
在这里插入图片描述
如果希望原序列不变,可以调用replace_copy,它额外接受第三个迭代器参数,指出调整后序列保存的位置
在这里插入图片描述
3、重排容器元素的算法
sort算法利用元素类型的<运算符来重排输入序列中的元素
假如我们要分析一个故事里的单词,有的单词重复出现,这就需要我们调用sort来排序,调用unique来“消除重复单词”,最后调用erase删除重复单词
给定的输入:
在这里插入图片描述
消除重复单词
在这里插入图片描述
sort接受两个表示范围的迭代器
完成sort后:
在这里插入图片描述
使用unique
调用unique后:
在这里插入图片描述
unique返回的迭代器指向最后一个不重复元素之后的位置
标准库算法对迭代器而不是容器操作,算法不能直接添加或删除元素
使用容器操作删除元素
调用erase才能真正删除元素
调用erase后:
在这里插入图片描述
即使序列中没有重复单词,调用erase也是安全的
10-3定制操作
1、向算法传递函数
在调用elimDups后,还要输出vector中的内容。输出时,单词按长度排序,相同长度的又按照字典顺序排序
这时,将调用sort的第三个参数是谓词的重载版本
谓词谓词是一个可调用的表达式,其返回结果是一个能用作条件的值
一元谓词:只接受一个参数
二元谓词:接受两个参数
元素类型必须能转换为谓词参数的类型,因为接受谓词参数的算法对元素调用谓词
将isShorter传递给sort
在这里插入图片描述
在这里插入图片描述
这时,sort按单词长度排序
排序算法
为了使相同长度的单词排序,可以调用stable_sort算法
在这里插入图片描述
2、lambda表达式
对程序继续修改,只输出长度大于给定长度的单词
在这里插入图片描述
标准库find_if算法用来查找第一个具有特定大小的元素
find_if接受一对迭代器,表示一个范围,它的第三个参数是一个谓词。它返回第一个使谓词不为0的元素,如果不存在这样的元素,则返回第二个迭代器
介绍lambda
对于一个对象或表达式,如果可以对其使用调用运算符,则称它为可调用的
在这里插入图片描述
一个lambda表达式表示一个可调用的代码单元,相当于一个未命名的内联函数
一个lambda具有一个返回类型,一个参数列表和一个函数体
lambda表达式的形式:
在这里插入图片描述
在这里插入图片描述
向lambda传递参数
lambda不能有默认参数
一个与isShorter功能相同的lambda:
在这里插入图片描述
用此lambda调用stable_sort:
在这里插入图片描述
使用捕获列表
lambda捕获sz:
在这里插入图片描述
我们可以在[]中提供一个以都喊分隔的名字的列表,这些名字都是它所在的函数中定义的
此时,如果提供了一个空捕获列表,则会产生编译错误:
在这里插入图片描述
调用find_if
在这里插入图片描述
这里的find_if返回一个迭代器,这个迭代器指向第一个长度不小于给定参数sz的元素,如果这样的元素不存在,则返回words.end()的拷贝
使用find_if返回的迭代器计算从它开始,到word结尾有多少个元素
在这里插入图片描述
在这里插入图片描述
for_each算法
解决问题的最后一步是打印长度大于sz的元素
for_each接受一个可调用对象,并对输入序列中的每个元素都调用此对象
在这里插入图片描述
捕获列表只用于lambda所在函数的局部非静态变量
lambda可以直接使用局部静态变量和它所在函数之外声明的名字
完整的biggies在这里插入图片描述
3、lambda捕获和返回
当向函数传递一个lambda时,同时定义了一个新类型和一个该类型的对象:传递的参数就是此编译器生成的类类型的未命名对象
在这里插入图片描述
值捕获
变量捕获的方式可以是值或引用
被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝
在这里插入图片描述
引用捕获
例如:
在这里插入图片描述
如果我们采用引用的方式捕获变量,则必须确保引用的对象在lambda执行的时候是存在的
lambda捕获的都是局部变量,这些变量在函数结束后就不存在了
如果lambda在函数结束后才执行,引用指向的局部变量已经消失
biggise接受一个ostream的引用,输出数据,并用空格隔开:
在这里插入图片描述
我们不能拷贝ostream对象,只能捕获其引用(或指向os的指针)
如果函数返回一个lambda,与函数不能返回几部变量的引用类似,此lambda不能包含引用捕获
隐式捕获
可以让编译器根据lambda中的代码来推断我们要使用哪些变量
为指示编译器推断捕获列表,可以在捕获列表中写一个&或=
&告诉编译器采用捕获引用方式
=表示采用值捕获方式
例如:
在这里插入图片描述
如果希望对一部分变量采用值捕获,对其它变量采用引用捕获,可以混合使用隐式捕获和显式捕获
例如:
在这里插入图片描述
在这里插入图片描述
混合使用时,捕获列表中第一个元素必须是一个&或=
混合使用时,显式捕获的变量必须使用与隐式捕获的变量不同的方式
即:如果隐式捕获采用引用捕获,则显式捕获的变量必须采用值捕获,不能用&
如果隐式捕获采用值捕获,则显式捕获的变量必须是引用捕获,必须用&
在这里插入图片描述
可变lambda
如果希望能改变一个呗捕获变量的值,就必须在参数列表首加上关键字mutable
例如:
在这里插入图片描述
一个引用捕获的变量能否被改变依赖于,此引用是否指向const:
在这里插入图片描述
指定lambda的返回类型
默认情况下,如果一个lambda包含return之外的任何语句,则编译器假定此lambda返回void
使用标准库transform和一个lambda将输入序列中的负数替换成其绝对值:
在这里插入图片描述
transform接受三个迭代器和一个可调用对象,前两个迭代器表示输入序列,第三个迭代器表示目的位置
如果将lambda中的语句修改成等价的if语句:
在这里插入图片描述
编译器推断返回的是void,实际却返回了int
如果要定义lambda的返回类型,就必须使用尾置返回类型:
在这里插入图片描述
4、参数绑定
如果一个操作需要在很多地方使用,或者需要很多语句才能完成,直接使用函数比用lambda表达式更好
如果lambda的捕获列表为空,通常可以用函数来代替它
但是,对于捕获局部变量的lambda,用函数来替换它不是那么容易
例如:
在这里插入图片描述
我们不能用这个函数作为find_if的参数,因为find_if接受的是一元谓词,即可调用对象必须只接受一个参数
要用check_size函数替换lambda,必须解决向形参传递sz的问题
标准库bind函数
bind定义在头文件functional中,它相当于一个函数适配器
bind接受一个可调用对象,生成一个新的可调用对象来使用原对象的参数列表
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
绑定check_size的sz参数
在这里插入图片描述
调用check6时,必须传递给它一个string类型的参数,check会把此参数传递给check_size
调用check6:
在这里插入图片描述
在这里插入图片描述
使用placeholders名字
名字_n都定义在一个名为placeholders的命名空间里,而这个命名空间本身定义在std命名空间中。
在这里插入图片描述
也可以这样:
在这里插入图片描述
bind的参数
我们可以用bind修正参数的值,也可以用bind绑定可调用对象中的参数或重新安排参数的顺序
在这里插入图片描述
在这里插入图片描述
bind重排参数顺序
在这里插入图片描述
在第一个sort调用isShorter(A,B)相当于在第二个函数中调用isShorter(B,A)
绑定引用参数
有事我们希望对参数以引用方式传递,或参数不能拷贝
在这里插入图片描述
在这里插入图片描述
可以编写一个函数,完成同样的功能
在这里插入图片描述
但不能直接用bind来代替对os的捕获:
在这里插入图片描述
原因在于bind拷贝其参数,而ostream不能被拷贝
如果希望给bind传递一个对象,而又不拷贝它,就必须使用标准库ref函数
在这里插入图片描述
在这里插入图片描述
10-4再探迭代器
除了为每个容器定义的迭代器外,在头文件iterator中还有几种额外的迭代器
在这里插入图片描述
在这里插入图片描述
1、插入迭代器
插入器是一种迭代器是一种迭代器适配器,它接受一个容器,生成一个迭代器,能实现向容器添加元素
通过一个插入迭代器赋值时,该迭代器调用容器操作来向给定容器的指定位置插入一个元素
在这里插入图片描述
当调用inserter(c,iter)时,我们会得到一个迭代器,接下来使用它时,会将元素插入到iter所指元素之前
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
front inserter会将插入的元素的顺序颠倒,inserter则不会
2、iostream迭代器
istream_iterator操作
当创建一个流迭代器时,必须指定迭代器将要读写的对象的类型
istream_iterator要读取的类型必须定义了>>运算符
例如:
在这里插入图片描述
eof被定义为一个空的istream_iterator,从而可以当做一个尾后迭代器来使用
对于一个绑定到流的迭代器,一旦其关联的流遇到文件尾或IO错误,迭代器的值就与尾后迭代器相等
在这里插入图片描述
在这里插入图片描述
使用算法操作流迭代器
例如:
在这里插入图片描述
istream_iterator允许使用懒惰求值
当我们将一个istream_iterator绑定到一个流时,标准库并不保证迭代器立即从流读取数据,直到我们使用迭代器时才真正读取
对大多数程序来说,立即读取或是推迟读取没有什么区别
但是,如果创建了一个istream_iterator,没有使用就销毁了
ostream_iterator操作
我们可以对具有输出运算符的类型定义ostream_iterator
当创建一个ostream_iterator时,我们可以提供第二参数——一个C风格字符串
必须将ostream_iterator绑定到一个指定的流,不允许空的或表示尾后位置的ostream_iterator
在这里插入图片描述
例如:
在这里插入图片描述
可以忽略引用和递增运算:
在这里插入图片描述
在这里插入图片描述
还可以用copy来打印vec中的元素:
在这里插入图片描述
使用流迭代器处理类类型
改写书店程序:
在这里插入图片描述
3、反向迭代器
递增一个反向迭代器(++)会得到上一个元素
递减一个反向迭代器(–)会得到下一个元素
反向迭代器也有const和非const版本
在这里插入图片描述
逆序打印vec中的元素:
在这里插入图片描述
sort递减排序:
在这里插入图片描述
反向迭代器需要递减运算符
forward_list不支持递增递减运算符,不可能在一个流中反向移动,因此,不能从它们创建反向迭代器
反向迭代器和其它迭代器的关系
假定有一个名为line的string,保存由逗号分隔的单词列表,希望得到第一个单词:
在这里插入图片描述
comma指向找到的第一个逗号
在这里插入图片描述
如果输入:在这里插入图片描述
程序会打印TSAL,因为使用的反向迭代器,所以会反向处理string
要把comma转换回一个普通迭代器,就要调用reverse_itertor的base成员函数在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
10-5泛型算法结构
在这里插入图片描述
1、 5类迭代器
一些操作所有迭代器都支持,另一些操作只有特定类别的迭代器才支持
ostream_iterator只支持递增、解引用和赋值,vector、string和deque除支持这些操作外,还支持递减、关系和算术运算
迭代器类别
在这里插入图片描述
输入迭代器只用于顺序访问,不能保证它的状态可以保存下来并用来访问元素。
输入迭代器只能用于单遍扫描算法。算法find和accumulate要求输入迭代器,istream_iterator是一种输入迭代器
在这里插入图片描述
只能向一个输出迭代器赋值一次,输出迭代器只能用于单遍扫描算法
用作目的位置的迭代器通常都是输出迭代器。copy函数的第三个参数就是输出迭代器,ostream_iterator也是输出迭代器
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
算法sort要求随机访问迭代器,array、deque、string、vector的迭代器都是随机访问迭代器,用于访问内置数组的元素的指针也是随机访问迭代器
2、算法形参模式
大多数算法具有如下形式之一:
在这里插入图片描述
接受单个目标迭代器的算法
dest参数是一个表示算法可以写入的目的位置的迭代器
算法假定:按需要写入数据,不管写入多少个元素都是安全的
接受第二个输入序列的算法
如果一个算法接受beg2和end2,则第一个范围是[beg,end),第二个范围是[beg2,end2)
3、算法命名规范
一些算法用重载函数传递一个谓词
例如:
在这里插入图片描述
_if版本的算法
在这里插入图片描述
两个算法不是重载
区分拷贝元素和不拷贝元素的版本
例如:
在这里插入图片描述
一些算法同时提供_copy和_if版本:
在这里插入图片描述
10-6特定容器算法
list,forward_list定义了独有的sort、merge、remove、reverse和unique
在这里插入图片描述
在这里插入图片描述
splice成员
链表类型还定义了splice算法:
在这里插入图片描述
链表特有的算法会改变容器在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值