C++泛型算法

1、泛型算法初始

标准库算法都是对一个范围内的元素进行操作(除了少数例外)。并将此范围称为“输入范围”,而且总是使用前两个参数来表示次范围,这两个参数分别代表,要处理的首元素和尾元素之后位置的迭代器。

1.1、只读算法
只会读取输入范围内的元素,而从不改变元素

find
int sum = accumulate(c.begin(),c.end(),0);
string sum = accmulate(strc.begin(),strc.end(),string(""));
string sum = accmulate(strc.begin(),strc.end(),"");//错误

对于只读,不改变元素的算法,通常使用cbegin(),cend()

操作两个序列的算法

//c2的元素数目至少应该等于c1。
//
equal(c1.begin(),c1.end(),c2.begin());

1.2、写容器元素算法
算法不会执行容器操作,因此它们不可能改变容器大小。

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


//插入迭代器
//back_inserter
vector<int> vec;
auto it = back_inserter(vec);
*it = 42;
//常常用back_inserter创建一个迭代器
fill_n(back_inserter(vec),vec.size(),0);

拷贝算法

//copy
int a1[] = {1,2,3,4,5,6,7,8,9};
int a2[sizeof(a1)/sizeof(*a1)];
auto ret = copy(begin(a1),end(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的拷贝。

1.3、排序算法

sort(c.begin(),c.end());//对列表c中的元素重新排序

auto end_unique = unique(c.begin(),c.end()); //消除重复元素

2、定制操作

2.1、向算法传递函数

bool isShorter(const string &s1, const string &s2)
{
    return si.size()< s2.size();
}

sort(c.begin(),c.end(),isSorter);

2.2、lambda表达式

一个lambda表达式表示一个可调用的代码单元。我们可以将其理解为一个未命名的内联函数。

[capture list](parameter list) -> return type{function bodt}
[捕获列表]为lambda所在函数中定义的局部变量的列表(通常为空)
(形参列表) 参数列表
return type 返回类型
function body 函数体

[](const string &s1,const string s2){return s1.size()//可以忽略参数列表和返回类型,但必须包含捕获列表和函数体
auto f = []{return 42;};

隐式捕获

[=](const string &s1){return s.size() >= sz;};//=表示值捕获方式
[&](const string &s1){return s.size() >= sz;};//&引用捕获

//混合使用隐式捕获和显式捕获,&隐式捕获,引用捕获,c显式捕获,值捕获
[&,c](const string &s){os<<s<<c};

使用混合捕获方式时,捕获方式不能相同。即,隐式捕获为值捕获时,显式捕获必须为引用捕获方式。

可变lambda

size_t v1 = 42;
auto f= [v1]()mutable{return ++v1};

指定lambda返回类型

[](int i) -> int{if(i<0) return -i;else return i;};

2.3、参数绑定

bind函数

//arg_list是用逗号分隔的参数列表。对应给定的callable的参数。
auto newCallable = bind(callable, arg_list);
auto check6 = bind(check_size, _1,6);
string s = "hello";
bool b1 = check6(s);//调用check6(s)时会调用check_size(s,6);

使用placeholders名字
名字_n都定义在一个名为placeholders的命名空间中,这个命名空间本身定义在std命名空间中。为了使用这些名字,两个命名空间都要写上。bind、placeholders都定义在functional头文件中。

3、迭代器

  • 插入迭代器(insert iterator),可用来向容器中插入元素
  • 流迭代器(stream iterator),绑定到输入输出流上,可用来遍历所关联的IO流。
  • 反向迭代器(reverse iterator),向后移动而不是向前移动,除了forward_list都有反向迭代器
  • 移动迭代器(move iterator),移动元素。

3.1、插入迭代器

back_inserter
front_inserter
inserter

3.2、iostream 迭代器
istream_iterator读取输入流
ostream_iterator输出流写数据

istream_iterator<int> int_it(cin); //从cin读取int
istream_iterator<int> eof; //尾后迭代器
vector<int> vec(int_it,eof);

vector<int>::iterator myiter;
for(myiter = vec.begin();myiter!=vec.end();myiter++)
{
    cout << *myiter << endl;
}

注:eof被定义为空迭代器,对于一个绑定到流的迭代器,一旦其关联的流遇到文件尾或遇到IO错误,迭代器的值就与尾后迭代器相等。

//输出流操作
const char *str = {"hello"};
ostream_iterator<char> out_iter(cout,"\n");

for(int i = 0;i<5;i++)
{
    out_iter = str[i];
}

3.3、反向迭代器
反向迭代器是在容器中从尾元素向首元素反向移动的迭代器。
在反向迭代器中递增递减操作的含义会跌倒过来。
递增一个反向迭代器(++it)会移动到前一个元素;递减一个反向迭代器会移动到一个元素。

//反向迭代器
int arr[] = {1,2,3,4,5,6,7};
size_t size = sizeof(arr)/sizeof(int);
vector<int> vet_int(arr,arr+size);
vector<int>::reverse_iterator iter;
for(iter = vet_int.rbegin();iter!=vet_int.rend();iter++)
{
    cout << *iter << endl;
}

4、泛型算法结构

4.1、5类迭代器
算法所要求的迭代器操作可以分为5个迭代器类别

  • 输入迭代器
  • 输出迭代器
  • 前向迭代器
  • 双向迭代器
  • 随机访问迭代器

第二种算法分类方式是按照是否读、写或者重排序序列中的元素

4.2、算法形参模式
大多数算法具有如下4中形式

//alg算法名字
alg(beg,end,other args);
alg(beg,end,dest,other args);
alg(beg,end,beg2,other args);
alg(beg,end,beg2,end2,other args);

4.3、算法命名规范
一些算法使用重载形式传递一个谓词

unique(beg,end);//使用==运算符比较元素
unique(beg,end,compare);//使用compare比较

_if版本的算法
接受一个元素值的算法通常有一个不同名的版本,该版本接受一个谓词代替元素

find(beg,end,val);//查找范围内val第一次出现的位置
find(beg,end,pred);//查找第一个令pred为真的元素
//都是用来查找出现的位置

区分拷贝版和非拷贝版

reverse(beg,end);//不拷贝,只反转
reverse_copy(beg,end,dest);//逆序拷贝到dest

5、特定容器的算法

list, forward_list定义了独有的sort, merge, remove, reverse 和 unique。通用版本要求随机反问迭代器,因此不能用于list和forwar_list。

对于list和forward_list应该优先使用成员函数版本的算法而不是通用算法

你可能感兴趣的:(c++)