algorithm头文件中定义了大多数算法。
这些算法并不直接操作容器,而是遍历由两个迭代器指定的一个元素范围,对其中的每个元素进行一些处理。
迭代器令算法不依赖于容器类型,但是算法依赖于元素类型的操作。大多数算法都使用了一个(或多个)元素类型上的操作,因此,除了默认操作符(==,<,>)外,我们还可以使用自定义的操作来代替默认操作。
注意:泛型算法本身不会执行容器的操作,它们只会运行于迭代器之上,执行迭代器的操作。
一、泛型算法分类
理解算法最基本的方法就是了解它们是否读取元素、改变元素或是重排元素顺序。
1、只读算法
只会读取其输入范围内的元素,而从不改变元素的算法称为只读算法。
查找特定元素:
find(vec.cbegin(), vec.cend(), val);
数特定元素出现的次数:
count(vec.cbegin(), vec.cend(), val);
范围内元素求和:
accumulate(vec.cbegin(), vec.cend(), 0);//第三个参数为和的初值
比较两个序列是否相等:
accumulate(vec.cbegin(), vec.cend(), 0);//第三个参数为和的初值
2、写容器元素的算法
写容器元素算法可以将新值赋予序列中的元素。
给定首尾迭代器,向容器范围内写元素:
fill(vec.begin(), vec.end(), val);//将vec容器中所以元素置为val
给定首迭代器和要写入的元素数,向容器范围内写元素:
fill_n(vec.begin(), n, val);//将vec.begin()开始的n个元素置为val
给定首尾迭代器,拷贝算法:
copy(roster1.cbegin(), roster1.cend(), roster2.begin());//将roster1中的所有元素拷贝到roster2.begin()开始的位置
给定首迭代器和要拷贝的元素数,拷贝算法:
copy_n(roster1.cbegin(), n, roster2.begin());//将roster1中roster1.cbegin()开始的n个元素拷贝到roster2.begin()开始的位置
替代算法:
replace(ilist.begin(), ilist.end(), 0, 42);//将ilist中所有的0元素替换为42
替代拷贝算法:
replace_copy(ilist.cbegin(), lilist.cend(), back_inster(ivec), 0, 42);//在ivec中插入ilist的一份拷贝,但其中所有的0元素都被替换为42
3、重排容器元素的算法
利用元素类型<运算符进行排序的算法:
清除相邻重复项的算法:
将sort和unique结合使用可以实现序列清除重复项的排序。
二、定制操作
对于要比较元素的算法,默认使用的是<或==运算符。但在一些特殊场合,需要自己定义操作来代替默认运算符。
1、向算法传递函数——谓词
谓词是一个可调用的表达式,其返回结果是一个能用作条件的值。
例子:接受一个二元谓词参数的sort版本用这个谓词代替<来比较元素。我们可以自定义sort的排序规则,比如按元素长度进行排序。
首先定义一个函数,它输入两个string对象,返回一个布尔量来表示这两个string对象的长度关系。
bool isShorter (const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
这个isShorter函数就可以作为谓词,在sort的一个3输入参数的重载函数中使用。
sort(words.begin(), words.end(), isShorter);//由短到长排序words
2、lambda表达式
a)、问题的引出
带谓词的查找函数:
find_if(words.cbegin(), words.end(), isFinded);
由于find_if接受一元谓词,也就是说我们传递给find_if的任何函数都必须严格接受一个参数,以便能用来自输入序列的一个元素(而不是一个元素和一个长度)调用它。
但事实上,一个isFinded函数的实现需要两个参数,一个string和一个长度。因此,isFinded函数不能成为一元谓词而在find_if函数中作为判断条件使用。
b)、使用lambda表达式
除了函数,任何“可调用对象”都可以作为谓词使用。对于一个对象或一个表达式,如果可以对其使用调用运算符,则称其为可调用的。
C++中共存在4种可调用对象:函数、函数指针、重载了函数调用运算符的类、lambda表达式。
一个lamdba表达式表示一个可调用的代码单元,我们可以将其理解为一个未命名的内联函数。
形式:[capture list](paramater list) -> return type{function body}
其中,capture list是一个lambda所在函数中定义的局部变量的列表;paramater list是参数列表;return type表示返回值类型;function body是函数体。
注意:lambda必须使用尾置返回来指定返回类型。
我们可以忽略参数列表和返回类型,但必须永远包括捕获列表和函数体:
auto f = [] { return 42; };
c)、向lambda传递参数
与普通函数类似,lambda表达式的实参和形参必须匹配。
与普通函数不同,lambda表达式不能有默认参数,也就是说,一个lambda调用的实参数目永远与形参数目相等。
例子:
与isShorter函数功能类似的lambda:
[](const string &a, const string &b)
{ return a.size() < b.size();}
我们可以在stable_sort中调用该lambda代替来isShorter函数:
stable_sort(words.begin(), word.end(),
[](const string &a, const string &b)
{ return a.size() < b.size();});
d)、使用捕获列表
对于a)中提出的问题,怎样构造一个表达式,可以将输入序列中每个string长度与给定的长度进行比较?
事实上,我们可以使用捕获列表将这个给定的长度(设为sz)捕获,作为lambda表达式的一部分。
[sz](const string &a)
{return a.size() >= sz;};
使用此lambda,我们就可以查找第一个长度大于等于sz的元素:
auto wc = find_if(word.begin(), words.end(),
[sz] (const string &a)
{ return a.size() >= sz;});
注意:调用find_if的函数必须具有形参sz,否则lambda将无法捕获sz,造成错误。