C/C++基础----泛型算法
算法不依赖与容器(使用迭代器),但大多数依赖于元素类型。如find需要==运算符,其他算法可能要求支持<运算符。
算法永远不会执行容器的操作,永远不会改变底层容器的大小(添加或删除元素)。
accumulate(v.cbegin(), v.cend(), string(“ ”)) 算法累加运算符,第3个参数的类型决定了使用哪个+号运算符。
equal(v1.cbegin(), v1.cend(), v2.cbegin()),逐个比较两个序列。第二个序列至少与第一个序列一样长。
容器和元素类型都不必一样,只要支持==符号比较两个元素类型。
那些只接受单一迭代器来表示第二个序列的算法,都假定第二个序列至少与第一个等长。
确保算法不会访问第二个序列中不存在的元素是程序员的责任。
算法不检查写操作面,向目的位置迭代器写入数据的算法
auto it = back_inserter(vec);//返回插入迭代器,使用它赋值时会调用push_back将元素添加到容器。
fill_n(it,10,0); //添加10个元素到vec。
很多算法提供拷贝版本,返回的是一份拷贝,原序列并未改变。
sort(words.begin(), words.end());//排序
auto end_unique = unique(words.begin(), words.end());//去重,返回不重复区域后面一个位置
words.erase(end_unique,words.end());//算法不能直接增删,调用erase删除多余元素
即时words没有重复元素,此操作也是安全的
谓词(predicate)
可调用的表达式,返回结果是一个能用作条件的值。
用谓词替代算法默认的操作,如定义二元isShorter函数,替代sort默认的<比较元素。
sort(words.begin(), words.end(), isShorter);
stable_sort(words.begin(), words.end(), isShorter);//维持相等元素的原有排序
partition将容器划分成两部分,true的在前。partition_stable 维持原有元素的顺序
可调用对象
函数、函数指针、函数对象,lambda表达式(可理解为一个未命名的内联函数)
[capture list] (parameter list) -> return type {function body}
其中参数列表和返回类型可以省略,但是捕获列表和函数体必须永远包含。
忽略参数列表等价于空参数列表
忽略返回类型,如果函数体只有一个return语句则从表达式的类型判断;如包含其他任意语句,则返回void。
lambda
不能有默认参数
lambda只有在其捕获列表中的捕获一个它所在函数的局部变量,才能再函数体中使用该变量。仅限局部非static变量,局部static和所在函数外声明的变量可以直接使用。
make_plural(count, “word”, “s”) //区分单复数,count为1则单数
find_if(words.begin(), words.end(), [sz](const string &a){return a.size() >=sz;});
for_each(wc,words.end(),[](const string &s){cout<<s<<” ”;});
排序-去重-删除多余-按长度排序-找到长度大于sz的起点,打印
值捕获
前提是变量可拷贝,被捕获的变量的值在lambda创建时拷贝,而不是调用时。
引用捕获
需要保证捕获的局部变量的有效性,也可以从函数返回lambda,不能包含局部变量的引用捕获。如果可能,尽量避免捕获指针或引用。
lambda捕获列表 |
|
[] |
空捕获列表 |
[names] |
名字列表,默认都是被拷贝 |
[&] |
隐式捕获列表,都采用引用捕获 |
[=] |
隐式捕获列表,都采用值捕获 |
[&,identifier_list] |
除个别值捕获 |
[=, identifier_list] |
除个别引用捕获 |
可变lambda
默认不会改变被捕获的变量的值,如希望改变,使用mutable。
对于只在一两个地方使用的简单操作,lambda表达式是最有用的。
对于在很多地方使用的相同操作,或者一个操作需要很多语句才能完成,通常定义一个函数更好。
捕获列表为空的lambda,通常可以用函数替代;
而对于有捕获变量的,就不那么容易了。会面临谓词参数个数不一致的问题。
bind函数(函数适配器)
接受一个可调用对象,生成一个新的可调用对象来适应原对象的参数列表。
auto newCallable = bind(callable, arg_list);
arg_list对应给callable的参数。其中可能包含_n形式的名字,表示占据了传递给newCallable参数的第n个位置。
bool check_size(const string& s, string::size_type sz) {return s.size() >= sz;}
auto check6 = bind(check_size, _1, 6);//
bind调用只有一个占位符,便是check6只接收一个参数,占位符出现在第一个arg_list的第一个位置,表示check6此参数对应check_size的第一个参数const string&。
auto g =bind(f, a, b, _2, c, _1);//f有5个参数,其中_1和_2分别对应g的第1个和第2个参数
g(_1,_2)映射为f(a, b, _2, c, _1)
需要包含命名空间using std::placeholders::_1;
using namespace std::placeholders;
对于不是占位符的参数,默认是拷贝到bind返回的可调用对象中的,有时候需要用引用方式传递ref(os),cref()生成const引用。
bind ref cref都在头文件functinal里。
注:bind1st和bind2nd,分别只能绑定第一个或第二个参数,由于局限性强已经在新标准中弃用。binder1st和binder2nd也类似,只不过他们是模板类需要指定op的类型
bind1st(op, value)(x) -> op(value,x)
bind2nd(op,value)(x) -> op(x,value)
其他迭代器
插入迭代器:绑定到容器上,可以向容器插入元素
流迭代器:绑定到输入输出流,可以遍历关联的IO流,只要定义了<<或>>运算符的对象
反向迭代器:向后移动,forward_list没有
移动迭代器:不是拷贝其中的元素,而是移动move它们。
插入迭代器
*it, ++it, it++ 不会对it做任何事情,返回it
back_inserter push_back
front_inserter push_front
inserter insert
如inserter生成的迭代器做如下赋值操作 *it = val;
效果相当于以下代码
it = c.insert(it, val);//it指向新加入的首元素
++it;
使用不同的插入迭代器,最后生成的序列顺序会不同。
流迭代器
创建时必须制定将要读写的对象类型,可以在创建时将其绑定到一个流上,也可以默认初始化迭代器当尾后迭代器使用。
istream_iterator<int> in_iter(cin);
istream_iterator<int> eof;
while(in_iter!=eof)
vec.push_back(*in_iter++);
可以改成成如下形式
istream_iterator<int> in_iter(cin), eof;
vector<int> vec(in_iter, eof); //从迭代器范围构造vec
从流中读取的数据来构造vec
与某些算法结合,来处理流数据。输入流迭代器可以作为源迭代器,输入作为目标迭代器
如cout<<accumulate(in, eof, 0)<<endl;
istream_iterator允许懒惰求值,推迟中流中读取数据。标准库保证在第一次解引用前,从流中读取数据的操作已经完成。
如果迭代器可能销毁,或者两个对象同步使用一个流,那么何时读取就很重要了。
ostream_iterator允许第二个参数,是C风格的字符串,在输出每个元素之后都会打印此字符串。必须绑定到一个流。
*out, ++out, out++提供形式支持,不会做任何事情
ostream_iterator<int> out_iter (cout, “ “);
for (auto e : vec)
*out_iter++ = e;
cout<<endl;
其中*和++都可以忽略,但是推荐这种写法,可以保持与其他迭代器的使用保持一致,修改容易,而且对读者来说也更为清晰。
反向迭代器
使我们可以使用算法透明地向前或向后处理容器。
需要既支持++也支持--,forward_list和流迭代器不能创建。
当使用反向迭代器,在打印的时候会反向,需要使用base成员来转换。
[line.crbegin(), rcomma)和[rcomma.base(), line.end())指向line中相同的元素范围。
另外反向迭代器表示的范围是不对称的,当从普通迭代器初始化反向迭代器或是给其赋值的时候,其指向并不是相同的。
5种迭代器类型
迭代器类别 |
|
输入迭代器 |
只读,不写;单遍扫描,只能递增 |
输出迭代器 |
只写,不读;单遍扫描,只能递增 |
前向迭代器 |
可读写;多遍扫描,只能递增 |
双向迭代器 |
可读写;多遍扫描,可递增递减 |
随机访问迭代器 |
可读写;多遍扫描,支持全部迭代器运算 |
算法指明了迭代器参数的最小类别,如传递的迭代器参数基本更低会产生错误。
输入迭代器:
至少支持==, !=, 前后置++, 解引用*(赋值的右侧), ->
*it++保证是有效的,但是递增可能导致其他迭代器失效,不能保证输入迭代器的状态可以保存下来并用来访问元素。因此只能用于单遍扫描算法。
如find和accumulate支持istream_iterator
输出迭代器:(可看做输入迭代器的补集)
至少支持前后置++, 解引用*(赋值的左侧),只能向一个输出迭代器赋值一次,单遍扫描。
如copy第3个参数,ostream_iterator
前向迭代器:
replace, forward_list
双向迭代器:(--)
reverse
随机访问迭代器:
< <= > >= + += - -= 两个迭代器相减 下标运算符
sort array deque string vector 内置数组元素的指针
常见算法形参类型
alg(beg, end, other args);
alg(beg, end, dest, other args);
alg(beg, end, beg2, other args);
alg(beg, end, beg2, end2, other args);
向输出迭代器接入数据的算法都是假定目标空间足够。
单个beg2的算法假定从beg2开始的序列至少与序列1一样大。
算法命名规范
重载传递一个谓词
_if版本 将接受一个元素值版本改成接受一个谓词版本,使用不同的函数名避免可能的重载歧义。
copy版本
默认重排元素的算法写回输入序列,copy版本写到一个指定的输出目的位置。
链表类型特有算法
其他通用算法也可以用于链表,但是代价过高,需要交换元素。链表可以通过改变链接来完成交换,而不用真正交换元素。所以优先使用成员函数版本。
list forward_list成员函数版本算法(都返回void) |
|
lst.merge(lst2) |
合并,两个都必须是有序的,合并之后lst2为空 |
lst.merge(lst2,comp) |
同上,使用谓词给定的比较操作 |
lst.remove(val) |
erase删除元素 |
lst.remove_if(pred) |
同上 |
lst.reverse() |
反转序列 |
lst.sort() |
使用<或给定比较操作排序 |
lst.sort(comp) |
|
lst.unique() |
删除同一个值得连续拷贝 |
lst.unique(pred) |
同上,使用二元谓词 |
splice成员
lst.splice(args)或flst.splice_after(args)
(p, lst2) 将lst2中所有元素移动到lst中p之前或flst中p之后,元素将从lst2删除,不能为同一个链表
(p, lst2, p2) 将p2指向的元素移动到lst中,可以是相同的链表
(p, lst2, b, e) 将b和e之间指向的元素移动到lst中,可以是相同的链表,可以是同链表但不能包含p。
链表特有版本一个重要的区别是会改变底层的容器。
C/C++基础----泛型算法的更多相关文章
- C++基础之泛型算法
标准库并未给每个容器添加大量功能,因此,通过大量泛型算法,来弥补.这些算法大多数独立于任何特定的容器,且是通用的,可用于不同类型的容器和不同的元素. 迭代器使得算法不依赖容器,但是算法依赖于元素的类型 ...
- C++ 泛型算法
<C++ Primer 4th>读书笔记 标准容器(the standard container)定义了很少的操作.标准库并没有为每种容器类型都定义实现这些操作的成员函数,而是定义了一组泛 ...
- Chapter10:泛型算法
泛型算法的基础是迭代器. 迭代器令算法不依赖于容器,但是算法依赖于元素类型的操作.也即:算法永远不会执行容器的操作. 那么,如果想向容器中添加元素或者执行其他的一些操作呢?标准库提供了插入迭代器来完成 ...
- c++11の泛型算法
一.泛型算法泛型算法这个概念是针对容器操作的,我们知道,c++11的顺序容器有vector,list,deque等,对于这些容器,c++11并没给出相应的增删改查方法,而是定义了一组泛型算法 一般的泛 ...
- c++ primer 11 泛型算法
使用泛型算法必须包含头文件#inlucde <algorithm> 标准库还定义一组泛化的算术算法,其命名习惯与泛型算法相同,包含头文件#include <numeric> f ...
- Java面试宝典系列之基础排序算法
本文就是介绍一些常见的排序算法.排序是一个非常常见的应用场景,很多时候,我们需要根据自己需要排序的数据类型,来自定义排序算法,但是,在这里,我们只介绍这些基础排序算法,包括:插入排序.选择排序.冒泡排 ...
- C++的那些事:容器和泛型算法
一.顺序容器 1,标准库定义了3种类型的顺序容器:vector.list和deque.它们的差别主要在于访问元素的方式,以及添加或删除元素相关操作运算代价.标准库还提供了三种容器适配器:stack.q ...
- C++ Primer : 第十章 : 泛型算法 之 只读、写和排序算法
大多数算法都定义在<algorithm>头文件里,而标准库还在头文件<numeric>里定义了一组数值泛型算法,比如accumulate. ● find算法,算法接受一对迭代 ...
- 【STL】帮你复习STL泛型算法 一
STL泛型算法 #include <iostream> #include <vector> #include <algorithm> #include <it ...
随机推荐
- String比较相等的问题探索
String比较相等的问题探索 工作上,有个同事犯了个低级错误,把字符串的计较用了==.由于代码已经交付客户,上了生产环境,给公司带了了损失.于是看了他的代码,自己根据以前学的知识,写了几个小demo ...
- TClientDataSet的FileName属性
读取cds文件数据. FileName一定要在设计时输入,否则程序运行时,不会自动读取cds文件. 因为FORM创建时,数据集组件也相应创建,如果是在运行时 设置FileName,那么是在数据集组件创 ...
- ssh的配置,ssh打开密钥登陆,关闭密码登陆。
刚装玩fedora,那么我们就以fedora为例来说一下怎么配置: 1.先确认是否已安装ssh服务: [root@localhost ~]# rpm -qa | grep openssh-server ...
- Spring-data-jpa详解
转自:http://www.cnblogs.com/dreamroute/p/5173896.html
- 记python3 UnicodeEncodeError: 'latin-1' codec... 报错
python3用cx_Oracle查询oracle数据库并打印输出,在windows上执行没问题,打算放suse上跑的时候就遇到了打印中文UnicodeEncodeError: 'latin-1' c ...
- 中国教授在BlackHat现场演示破解SIM卡AES-128加密
使用一个PC和示波器克隆3G/4G SIM卡,破解过程只需十分钟.上海交大教授郁昱现场展示了如何成功复制SIM卡,以及一张克隆卡如何变更了支付宝的密码并潜在盗取账户资金. 破解SIM卡加密 今年二月, ...
- 使用 Git & Repo 下载代码
客户端安装 Git 安装 git,gitk 网络连接正常的情况下: $ sudo apt-get install git-core gitk git-gui 不能上网,有.deb安装包的,请执行: $ ...
- pyqt5:图片自适应QLabel大小和图片移除
参考链接: https://www.e-learn.cn/content/qita/669569 图片自适应QLabel大小 # coding=utf- import sys from PyQt5.Q ...
- vue-cli 添加到生产环境问题总结
1. 路径问题 部署到生产环境后的实际链接为: 服务器项目路径 + serviceUrl的路径 创建 GlobalConstant.js 分别配置 开发环境和生产环境的 路径 (注:此处生 ...
- C# 构建S7服务器 西门子的虚拟服务器 测试通讯 HslCommunication应用
本文将使用一个gitHub开源的组件技术来实现S7服务器的功能,使用的是基于以太网的TCP/IP实现,不需要额外的组件 github地址:https://github.com/dathlin/HslC ...