10.3 定制操作

news2024/7/4 6:30:51

文章目录

    • 向算法传递函数
      • 谓词
      • 排序算法
    • lambda表达式
      • 可调用对象
      • 介绍lambda
      • find_if 和 find_each的介绍
    • lambda的捕获和返回
      • 值捕获
      • 引用捕获
      • 隐式捕获
      • 可变lambda
      • 指定lambda返回的类型
      • 函数体
    • 参数绑定
      • 标准库bind函数
      • 占位符_n
      • 具体使用
      • bind的参数
      • 使用bind重排参数顺序
      • 绑定引用参数

向算法传递函数

可以重载sort的默认排序操作:

sort(beg,end,comp);
partition(beg,end,comp);//符合条件在前半段,不符合在后半段

此处的comp,我们将其称之为谓词。

谓词

谓词分为两种,一元谓词和二元谓词,分别接受一个参数和两个参数。
谓词可调用表达式,返回结果是作为条件的值。

//sort中可以设计一个二元谓词,重载sort函数,改变排序方法
	bool isshorter(const string& s1, const string& s2) {
		return s1.size() < s2.size();
	}
	sort(strs.begin(), strs.end(), isshorter);
	//partition接收一元谓词
	bool letterbiggerthanh(char a) { 
	return a > 'h';
	}
	string s = { "hhdyzuhyaaa" };
	partition(s.begin(), s.end(), letterbiggerthanh);
	cout << s << endl; //yuzydhhhaaa改变字典序
	s = { "hhdyzuhyaaa" };
	stable_partition(s.begin(), s.end(), letterbiggerthanh);
	cout << s << endl; //yzuyhhdhaaa不改变字典序

排序算法

stable_sort内部实现是归并排序,sort是快速排序。
stable_sort算法是稳定排序算法,维持了相等元素的原有顺序

lambda表达式

对于函数,严格接收一元或二元谓词,然而考虑到有些操作可能需要更多的参数,引入lambda表达式。

lambda表达式是可调用的代码单元,可以理解为未命名的内联函数。

可调用对象

对一个对象或表达式,可以对其使用调用运算符(一对圆括号()),则称它是可调用的。调用运算符中防置实参列表。

可调用对象有:函数、函数指针、重载调用运算符的类、lambda表达式等。

介绍lambda

对于函数,严格接收一元或二元谓词,然而考虑到有些操作可能需要更多的参数,引入lambda表达式。

lambda表达式是可调用的代码单元,可以理解为未命名的内联函数。

lambda的形式:

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

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

我们可以进行这么一个理解,()内部的值是算法默认传入的参数,我们如果需要额外的参数,就需要[],内部传入捕获的值。

比如我们可以完成对于上面sort的改写:

sort(strs.begin(), strs.end(), [](const string& s1, const string& s2){return s1.size() < s2.size()});

find_if 和 find_each的介绍

> find_if(beg,end,comp)  //找第一个符合条件的,返回其迭代器。
> for_each(beg,end,comp) //此算法接受一个可调用对象(comp),并对身处beg-end之间的元素调用此可调用函数。

lambda的捕获和返回

当定义一个lambda时,编译器生成一个与lambda对应的新的(未命名的)类类型。我们将在之后介绍这种类是如何生成的。目前,可以这样理解,当向一个函数传递一一个lambda时,同时定义了一个新类型和该类型的一个对象:传递的参数就是此编译器生成的类类型的未命名对象。类似的,当使用auto定义一个用lambda初始化的变量时,定义了一个从lambda生成的类型的对象。

默认情况下,从lambda生成的类都包含一个对应该lambda所捕获的变量的数据成员。类似任何普通类的数据成员,lambda的数据成员也在lambda对象创建时被初始化。

值捕获

#include<iostream>

using namespace std;

void func()
{
	int val = 42;
	auto f = [val]()->void {cout << val; };
	val = 0;
	f();
}

int main() 
{
	func();
	return 0;
}

输出结果:
在这里插入图片描述
由于被捕获变量的值是在lambda创建时拷贝,因此随后对其修改不会影响到lambda内对应的值。

引用捕获

#include<iostream>

using namespace std;

void func()
{
	int val = 42;
	auto f = [&val]()->void {cout << val; };
	val = 0;
	f();
}

int main() 
{
	func();
	return 0;
}

输出结果:
在这里插入图片描述
对于引用捕获,常用于一些无法进行拷贝的地方,如ostream,无法进行拷贝。
例子:

void biggies(vector<string>& words, vector<string>::size_type sz, ostream& os = cout, char c = ' ')
{
	for_each(words.cbegin(), words.cend(), [&os, c](const string& s) {os << s << c; });
}

如果我们捕获一个指针或迭代器,或采用引用捕获方式,就必须确保在lambda 执行时,绑定到迭代器、指针或引用的对象仍然存在。而且,需要保证对象具有预期的值。在lambda从创建到它执行的这段时间内,可能有代码改变绑定的对象的值。也就是说,在指针(或引用)被捕获的时刻,绑定的对象的值是我们所期望的,但在lambda执行时,该对象的值可能已经完全不同了。

隐式捕获

除了显式列出我们希望使用的来自所在函数的变量之外,还可以让编译器根据lambda体中的代码来推断我们要使用哪些变量。为了指示编译器推断捕获列表,应在捕获列表中写一个&或=。&告诉编译器采用捕获引用方式,=则表示采用值捕获方式。

例子:

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

当我们混合使用隐式捕获和显式捕获时,捕获列表中的第一一个元素必须是一个&或=。此符号指定了默认捕获方式为引用或值。

当混合使用隐式捕获和显式捕获时,显式捕获的变量必须使用与隐式捕获不同的方式。即,如果隐式捕获是引用方式(使用了&),则显式捕获命名变量必须采用值方式,因此不能在其名字前使用&。类似的,如果隐式捕获采用的是值方式(使用了=),则显式捕获命名变量必须采用引用方式,即,在名字前使用&。

构造捕获列表方式如下:

构造方式说明
[ ]空捕获列表。不使用lambda所在函数里的局部变量
[names]逗号分隔开的名字列表,是lambda所在函数里的局部变量,默认背靠背,如果名字前加了&,则表示引用捕获
[&]隐式捕获列表,引用捕获方式。
[=]隐式捕获列表,值捕获方式。
[&,identifier_list]混合使用隐式捕获和显式捕获。隐式捕获为引用捕获或,显示捕获为值捕获
[=,identifier_list]混合使用隐式捕获和显式捕获。隐式捕获为值捕获或,显示捕获为引用捕获

可变lambda

对于值被拷贝的变量,lambda不会改变其值(不是左值),如果希望能改变一个被捕获变量的值,要在参数列表首加上mutable。

int v1 = 42;
auto f = [v1]()mutable {return ++v1; }; //加mutable,v1可以修改,v1被拷贝传递进去,此时值为42
v1 = 0;
cout << f() << endl; //43
cout << v1 << endl; //0
v1 = 42;
auto f3 = [v1](){return ++v1; }; //报错,v1不是可修改左值
v1 = 0;
cout << f3() << endl;
cout << v1 << endl;
v1 = 42;
auto f2 = [&v1]() {return ++v1; }; //引用v1
v1 = 0;
cout << f2() << endl; //1
cout << v1 << endl; //1
return 0;

指定lambda返回的类型

lambda必须使用尾置返回。

当编写lambda只包含单一return语句时,编译器可以推算返回类型。当lambda函数体包含除return之外任何语句时,编译器假定此lambda返回void。

	transform(s.begin(), s.end(),s.begin(), [c](char s_c) {return (s_c > 'h'? s_c : 'h'); });
	std::cout << s << endl;
	transform(s.begin(), s.end(), s.begin(), [](char s_c) ->char{if (s_c > 'h') return s_c; else return 'h'; });
	std::cout << s << endl;
	transform(s.begin(), s.end(), s.begin(), [](char s_c) {if (s_c > 'h') return s_c; else return 'h'; });  //这个版本按理应当错误,但在vs2022可以通过
	std::cout << s << endl;

函数体

只对lambda所造函数中定义的非static的局部变量使用捕获列表,lambda可以直接使用局部static变量和它所在函数之外声明的名字,如定义在头文件中的名字。

参数绑定

lambda表达式不能有默认参数,lambda调用实参数目与形参数目相等

lambda适用于在少数地方使用,且函数体语句数目少的情况。

其他情况,如果lambda捕获列表为空,可以用函数替代。

如果使用find_if调用check_size函数,考虑find_if只接受一元谓词,需要解决向sz形参传递参数的问题。可以使用bind函数。

标准库bind函数

bind函数可以看作一个通用函数适配器,他接受一个可调用对象并生成一个新的可调用对象适应原对象参数列表。

头文件:

#include<functional> 

函数形式:调用newCallable时,newCallable调用callable,向callable传递arg_list中的参数

auto newCallable = bind(callable,arg_list); 
newCallable:可调用对象
callable:目标调用对象
arg_list:逗号分隔参数列表,对应callable参数。

占位符_n

arg_list中可能出现_n,其中n是整数,表示可调用对象中参数位置。这个参数是占位符,占据该位置,可以在函数调用时输入该参数。

名字_n定义在std::placeholders命名空间中,该命名空间在头文件#include<functional>中。使用_n需要分别对对应名字进行声明,或直接声明对应命名空间:

using std::placeholders::_1; //分别对对应名字进行声明
using namespace std::placeholders; //直接声明对应命名空间

建议直接使用后者。

具体使用

//eg:bind绑定check_size,并使用find_if调用
bool check_list(const string& a, int b) {
	return a.size() > b;
}
int main()
{
	vector<string> strs = {...};
	int sz = 5;
	//placeholders::_1指向传入参数const string& a,sz指向传入参数int b
	//对于checksz来说此时他仅仅接受一个参数,也就是placeholders::_1,也就是仅支持一元谓词
	auto checksz = bind(check_list, placeholders::_1, sz);
	auto iter = find_if(strs.begin(), strs.end(), checksz);
}

bind的参数

auto g = (f,a,b,_2,c,_1);
实际上调用g(x,y)
传入的数据是f(a,b,y,c,x)

使用bind重排参数顺序

	//从短到长进行排列。
	sort(words.begin(),words.end(),isShorter());
	//从长到短进行排列。
	sort(words.begin(),words.end(),bind(isShorter,_2,_1))

绑定引用参数

bind中不是占位符的参数被拷贝到bind返回的可调用对象中,如果希望采用引用的方式传递,必须使用标准库ref或cref函数。

所在头文件:#include<functional>
ref:采用引用方式传递参数。
cref:生成保存const引用的类。

例子:

ostream& print(ostream& os ,const string& s ,char c)
{
	return os << s << c;
}

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

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

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

相关文章

svg动画图形绘制

先介绍下绘制图形的标签 1&#xff1a;线段&#xff1a;line 2&#xff1a;矩形&#xff1a;rect 3: 圆形&#xff1a;circle 4&#xff1a;多边形&#xff1a;polyline&#xff08;不会自动连接起点和终点&#xff09; 5: 多边形:polygon (会自动连接起点和终点&#xff09; v…

SAP FICO 理解统驭科目记账与特殊记账

统驭科目记账与特殊记账 【背景】 统驭科目通常分为三类&#xff1a;资产&#xff08;A&#xff09;、客户&#xff08;D&#xff09;和供应商&#xff08;K&#xff09;&#xff0c;在创建会计科目时可在”控制数据“选项卡下进行选择。 在创建客户/供应商主数据的时候&#…

Vue插槽Slot的使用

1、认识插槽Slot 在开发中&#xff0c;我们会经常封装一个个可复用的组件&#xff1a; 前面我们会通过props传递给组件一些数据&#xff0c;让组件来进行展示&#xff1b;但是为了让这个组件具备更强的通用性&#xff0c;我们不能将组件中的内容限制为固定的div、span等等这些…

【零基础】学python数据结构与算法笔记15-欧几里得、RSA

文章目录前言95.欧几里得算法96.RSA算法介绍97.RSA算法测试98.算法课程总结总结前言 学习python数据结构与算法&#xff0c;学习常用的算法&#xff0c; b站学习链接 95.欧几里得算法 求最大公约数 欧几里得算法&#xff1a;gcd(a,b) gcd(b,a mod b) #mod取余 例&#xff1a…

04.自定义类型:结构体

1 结构体的声明 1.1 结构的基础知识 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。 1.2 结构的声明 struct tag { member-list;//成员列表 }variable-list;//变量列表 EG: 描述一位学生&#xff1a; struct Stu {char name[…

Comparable+Comparator+Cloneable接口

文章目录ComparableComparatorCloneable浅拷贝深拷贝Comparable 当我们需要对一个自己写的类进行排序(Collections.sort和Arrays.sort)的时候&#xff0c;,就要使用到Comparable接口。 该接口中有一个compareTo方法&#xff0c;该方法其实就是一比较规则。 public interface…

货币银行学

建立大脑知识库 外汇储备不宜太高&#xff0c;2022年是3万亿。 美元贬值&#xff0c;大宗商品会更贵。 大宗商品 [1] &#xff08;Commodities&#xff09;是指可进入流通领域&#xff0c;但非零售环节&#xff0c;具有商品属性并用于工农业生产与消费使用的大批量买卖的物质商…

Oracle Id生成算法 —— 雪花算法

背景 近几日&#xff0c;被主键ID生成折磨的不太行&#xff0c;于是就在寻找一种合适的主键生成策略&#xff0c;选择一种合适的主键生成策略&#xff0c;可以大大降低主键ID的维护成本。 主键ID生成方法 最常用的4种主键ID生成方法 UUID&#xff1a;全局唯一性&#xff0c…

【框架】Spring

1、IOC 1、自动化配置 xml文件 注册bean 属性注入&#xff1a;setter&#xff0c;构造方法&#xff0c;p命名空间&#xff0c;外部注入 复杂属性&#xff1a;对象ref&#xff0c;数组array&#xff0c;list&#xff0c;map 依赖注入&#xff1a;ctx.getBean()Java配置类 Conf…

InterruptedException异常解析

Either re-interrupt this method or rethrow the “InterruptedException”. 请重新中断此方法或重新引发“InterruptedException”。 文章目录问题描述问题解析sonar检测提示规则解决方案问题描述 public void run () {try {while (true) {// do stuff}}catch (InterruptedE…

webgl变换矩阵理论详解

文章目录前言矩阵运算矩阵加减矩阵数乘矩阵乘矩阵矩阵转置逆矩阵正交矩阵矩阵变换的一般规则行主序和列主序行向量和列向量复杂变换时的顺序变换矩阵进行图形变换uniform传递矩阵平移缩放旋转组合变换实例总结前言 在webgl中将图形进行平移、旋转、缩放等操作时可以在着色器中…

11.1 使用关联容器

文章目录关联容器的类型使用map使用set关联容器中元素是按关键字保存和访问的&#xff0c;支持高效关键字查找和访问。顺序容器中元素是按他们在容器中的位置保存访问的。关联容器有两个主要类型&#xff1a;set和map。 set&#xff1a;每个元素包含一个关键字&#xff0c;想知…

OPC实践:通过 python-docx 读取 docx 文档

概述 本文记录下列命令执行的过程&#xff0c;通过对过程中的关键步骤进行记录&#xff0c;掌握 python-docx 库中 opc 与 parts 模块的源码、以及加深对 OPC 的理解。 import docx# fp 为 docx 文件路径&#xff0c; docx 包含一个 hello 字符串、一张 jepg 图片及一个表格…

<Python的列表和元组>——《Python》

目录 1.列表 1.1 列表的概念 1.2 创建列表 1.3 访问下标 1.4 切片操作 1.5 遍历列表元素 1.6 新增元素 1.7 查找元素 1.8 删除元素 1.9 连接列表 2. 元组 1.列表 1.1 列表的概念 编程中, 经常需要使用变量, 来保存/表示数据. 如果代码中需要表示的数据个数比较少,…

初识 Bootstrap(前端开发框架)

初识 Bootstrap&#xff08;前端开发框架&#xff09;参考Bootstrap特点获取目录结构jQuery 与 Popper准备工作包含 jQuery 与 Poppermetabox-sizing基本模板无注释版本注释版本参考 项目描述Bootstrap 官方教程https://getbootstrap.net/docs/getting-started/introduction/百…

字节青训前端笔记 | HTTP 使用指南

本节课介绍 Http 协议的基本定义和特点&#xff0c;在此基础上&#xff0c;对于 Http 协议的发展历程及报文结构展开进一步分析。 从输入字符串到打开网页 输入地址浏览器处理输入信息浏览器发请求到达服务器服务器返回信息浏览器读取响应信息浏览器渲染页面加载完成 什么是…

KVM虚拟化简介 | 初识

目录 1、kvm架构 2、架构解析 3、kvm和qemu的作用 1、kvm架构 2、架构解析 从rhel6开始使用&#xff0c;红帽公司直接把KVM的模块做成了内核的一部分。xen用在rhel6之前的企业版中默认内核不支持&#xff0c;需要重新安装带xen功能的内核KVM 针对运行在x86 硬件上的、驻留在内…

配置 Git 连接 GitHub

文章目录0.安装 Git1.注册 GitHub 账号2.配置 Git 的用户名和邮箱3.为本机生成 SSH 密钥对4.将公钥拷贝到 GitHub 上5.测试0.安装 Git Git 官网链接&#xff1a;https://git-scm.com/ Git 官网下载链接&#xff1a;https://git-scm.com/downloads 1.注册 GitHub 账号 GitHu…

蓝桥杯STM32G431RBT6学习——定时器PWM输出

蓝桥杯STM32G431RBT6学习——定时器PWM输出 前言 PWM波输出作为定时器的一个常用功能&#xff0c;也属于高频的考点。从数据手册的定时器解析可以了解到&#xff08;上篇描述&#xff09;&#xff1a;除了基本定时器&#xff08;TIM6、7&#xff09;外&#xff0c;其他所有定…

全国产网管型工业交换机的几种管理方式

全国产网管型工业交换机按其字面上的意思&#xff0c;一是全国产化&#xff08;工业交换机&#xff09;&#xff0c;就是交换机内部95%以上元器件的国内生产制造&#xff0c;重要的硬件芯片&#xff0c;比如交换机芯片、管理芯片、接口芯片等必须是国内厂商在国内研发、生产、制…