泛型算法

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,造成错误。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值