C++泛型算法和lambda表达式

C++标准库提供一组算法,这些算法大多独立于任何特定的容器,它们是通用的(泛型的):可用于不同类型的容器和不同类型的元素。大多数的算法都定义在头文件algorithm中。

一般情况下,算法并不直接操作容器,而是遍历迭代器指定的元素范围进行操作。这里说明一下尾后迭代器,即指向容器最后一个元素的后面位置的迭代器,所以很多算法遍历时都是[begin,end)的前闭后开形式。许多算法在遍历迭代器时出现错误或者容器中查找不到指定元素时,也会返回尾后迭代器。

1、只读算法:find、count、equal、accumulate(头文件numeric)
①find算法
查看容器中是否包含特定的值
eg:find(vec.cbegin(), vec.cend(), val);//在vec中查找元素val,找到返回指向元素val的迭代器,否则返回vec.end(),查找范围:[begin,end)
②accumulate算法
对给定容器范围内的元素求和
int sum = accumulate(vec.begin(),vec.end(),0);//初值为0,求容器内所有元素的和

对于只读算法,不改变容器内的元素,通常使用cbegin(),cend()。c是const的意思。

2.写容器元素算法:fill,fill_n,back_inserter
说明:算法不会执行容器操作,因此它们自身不可能改变容器的大小。
①fill

fill(c.begin(),c.end(),0);//用0填充。

②fill_n

fill_n(dest,n,val);//dest指向一个元素,从dest开始的序列至少包含n个元素,保证写入指定个元素是安全的

③back_inserter 插入迭代器
插入迭代器,执行赋值运算时,会调用push_back将一个具体给定的值元素添加到容器中,返回一个与该容器绑定的插入迭代器

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

3、拷贝算法:copy,replace,replace_copy

//copy算法
int a1[] = {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算法
replace(c.begin(),c.end(),0,5);//将序列c中的0替换为5
//replace_copy,不改变原来的序列
replace_copy(c.begin(),c.end,back_inserter(ivec),0,5);//ivec是c的拷贝。

4、重排容器算法:sort,unique

sort(c.begin(),c.end());//对列表c中的元素重新排序
auto end_unique = unique(c.begin(),c.end()); //消除重复元素

lambda简单介绍
一个lambda表达式表示一个可调用的代码单元。可以将其理解为一个未命名的内联函数。与任何函数类似,一个lambda具有一个返回类型、一个参数列表和一个函数体;但是与普通函数不同的是,lambda可能定义在函数内部。一个lambda表达式具有如下形式:

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

capture list是捕获列表,可以捕获其所在函数的局部变量,捕获到的变量可以在lambda函数体中使用。注意:捕获列表只用于局部非static变量,对于局部static变量和该lambda所在函数之外说明的名字,不需要捕获可以直接使用。
paramenter list、function body与普通函数一样,分别表示参数列表和函数体;至于返回类型,lambda必须使用尾置返回来指定返回类型,什么是尾置返回,下面再说。

先看一个最简单的lambda表达式,忽略参数列表和返回类型,但是必须包含捕获列表和函数体:

auto f = [] { return 42; };

(auto 是C++ 11标准引入的类型说明符,让编译器分析表达式的类型,auto定义的变量必须初始化,VS2010的编译器已经支持auto)。
在这里,我们忽略了lambda的返回类型,lambda会根据函数体中的代码推断出返回类型。需要说明的是,如果lambda的函数体包含了任何单一的return语句之外的内容(上面的函数体就是单一的表达式),且未指定返回类型,则返回void。

变量的捕获方式有值捕获引用捕获。与函数参数值传递类似,值捕获的前提是变量可以拷贝,不同的是,传参是在函数调用时进行拷贝,而值捕获是在lambda创建时就已经拷贝。
对于无法拷贝的对象(如ostream的对象),我们只能使用引用捕获,需要注意的是,确保被引用的对象在lambda执行的时候是存在的。

void func()
{
	size_t v = 42;
	auto f1 = [v] { return v };	//值捕获
	auto f2 = [&v] { return v };//引用捕获
	v = 0;
	auto j1 = f1();	//j1为42
	auto j2 = f2();	//j2为0
}

隐式捕获
上面栗子中,我们在捕获列表中显式列出要捕获的变量,除此之外,还可以在捕获列表中写一个=或&来隐式捕获。=告诉编译器采用值捕获方式,而&表示采用引用捕获方式。

混合使用隐式捕获和显示捕获
另外,我们可以同时捕获多个变量,它们之间用逗号隔开。如果希望对一部分变量采用值捕获,另一部分采用引用捕获,可以混合使用隐式捕获和显示捕获,此时,捕获列表的第一个元素必须是一个&或=,表示默认捕获方式是引用捕获还是值捕获,而第二个元素捕获方式必须与第一个不同

尾置返回类型
上面说过,果lambda的函数体包含了任何单一的return语句之外的内容,且未指定返回类型,则返回void。

//错误:不能推断lambda的返回类型
transform(vec.begin(), vec.end(), vec.begin(), 
				[] (int i)
				 {
				 	 if (i < 0) 
				 	 {
						return -i; 
					 } 
					 else
					 {
					 	return i;
					 }
				  });

应改为:

transform(vec.begin(), vec.end(), vec.begin(), 
				[] (int i) -> int	//尾置返回类型
				 {
				 	 if (i < 0) 
				 	 {
						return -i; 
					 } 
					 else
					 {
					 	return i;
					 }
				  });

关于lambda表达式的介绍暂时到这里,下面用一个栗子应用C++泛型算法和lambda表达式:
①对输入的单词进行筛选,去掉重复的单词
②找出长度大于等于给定长度的单词
③将符合长度的单词按长度进行排序
④若长度相等,则按字典序

#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <algorithm>

using namespace std;

void elimDups(vector<string> &words)
{
	//sort算法:按字典序排序words,以便查找重复单词
	sort(words.begin(), words.end());
	//unique算法:重新排序,使每个单词只出现一次
	//unique返回指向不重复区域之后的第一个位置的迭代器
	vector<string>::iterator end_unique = unique(words.begin(), words.end());
	//使用vector的erase操作删除重复单词
	words.erase(end_unique, words.end());
}

void biggies(vector<string> &words, vector<string>::size_type sz)
{
	//将words按字典序排序,删除重复单词
	elimDups(words);
	//按长度排序,若长度相同则按字典序
	//stable_sort算法:
	//stable_sort比较两个元素时,会调用下面这个lambda表达式
	stable_sort(words.begin(), words.end(),
				[](const string &a, const string &b)
				{ return a.size() < b.size(); });
	//获取一个迭代器,指向第一个满足size >= sz 的元素
	//find_if算法:

	//方法1:显式捕获sz
	vector<string>::iterator it_first = find_if(words.begin(), words.end(),
									[sz](const string &a)
										{ return a.size() >= sz; });

	//方法2:隐式捕获sz,以值捕获方式。lambda体将拷贝所使用的来自所在函数的实体的值,即拷贝sz
	//vector<string>::iterator it_first = find_if(words.begin(), words.end(),
	//								[=](const string &a)
	//									{ return a.size() >= sz; });

	//计算满足size >= sz 的元素个数
	size_t count = words.end() - it_first;
	cout << " 长度大于 " << sz << " 的单词有: " << count << "个" << endl;
	cout << " 它们分别是: " << endl;
	//for_each算法:
	for_each(it_first, words.end(), 
		[](const string &s)
			{cout << s << " ";});
	cout << endl;
}

int main()
{
	vector<string> vec;
	string word;
	while(cin >> word)
	{
		vec.push_back(word);
	}
	biggies(vec, 3);//统计长度大于3的单词个数

	system("pause");
	return 0;
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值