#include<iostream>
#include<algorithm>
#include<numeric>
#include<vector>
#include<string>
#include<queue>
#include <functional>
#include<list>
#include<fstream>
using namespace std;
void test() {
int val = 42;
vector<int>vec;
//如果在vec中找到想要的元素,返回结果指向他,否则返回结果为vec.cend();
auto result = find(vec.cbegin(), vec.cend(), val);
string val = "a value";
vector<string>lst;
auto result = find(lst.cbegin(), lst.cend(), val);
int ia[] = { 23, 21, 46, 89, 90 };
int val = 83;
int* result = find(begin(ia), end(ia), val);
auto result = find(ia + 1, ia + 4, val);
//迭代器算法不依赖与容器但是算法依赖于元素类型的操作,算法永远不会改变底层容器的大小
//泛型算法
//只读算法
//一些算法只会读取其输入范围的元素,而从不改变元素
vector<int>vec;
int sum = accumulate(vec.cbegin(), vec.cend(), 0);
vector<string>s;
string sum = accumulate(s.cbegin(), s.cend(), string(""));
//错误 :const char* 上没有定义+运算符
string sum = accumulate(s.cbegin(), s.cend(), "");
//操作两个序列的算法
vector<int>roster1, vector<int>roster2;
equal(roster1.cbegin(), roster1.cend(), roster2.cbegin()); //用于确定两个序列是否具有相同的值
//写容器元素
fill(vec.begin(), vec.end(), 0); // 将每个元素重置为0
fill(vec.begin(), vec.end() + vec.size() / 2, 10);
//算法不检查写操作
vector<int>vec;
fill_n(vec.begin(), vec.size(), 0); //将所有元素置于0
//新手容易犯的错误
fill_n(vec.begin(), 10, 0); //修改vec中10个不存在的元素,这个 是未定义的
//back_inserter
/*
一种保证算法有足够空间来容纳输出数据的方法是使用插入迭代器,通常情况下,当我们使用一个迭代器对容器元素赋值时,
值被赋予迭代器所指向的元素,而当我们通过插入迭代器赋值的时候,一个与赋值号右侧值相等的元素被添加到容器
back_inserter接受一个指向容器的引用,返回一个与该容器绑定的迭代器当我们通过此迭代器赋值时,赋值运算符会调用
push_back将一个具有给定值得元素添加到容器中
*/
vector<int>vec;
auto it = back_inserter(vec);
*it = 42;
//我们通常使用back_inserter来创建一个迭代器
vector<int>vec;
fill_n(back_inserter(vec), 10, 0); //添加10个元素到vec
//拷贝算法
int a1[] = { 0, 1, 2, 3, 4,5, 6, 7 };
int a2[sizeof(a1) / sizeof(*a1)];
auto ret = copy(begin(a1), end(a1), a2); //把a1的内容拷贝给a2
//copy返回的是其目的位置迭代器(递增后)的值
//replace
vector<int>ilst;
replace(ilst.begin(), ilst.end(), 0, 42);
//如果我们希望保持原来的序列不变
vector<int>ivec;
replace_copy(ilst.cbegin(), ilst.cend(), back_inserter(ivec), 0, 42); //ivec包含ilst的一份拷贝
}
//重排容器的算法
void elimDups(vector<string>& words) {
sort(words.begin(), words.end());
auto end_unique = unique(words.begin(), words.end());
/*
unique并不是真的删除元素,他只是覆盖相邻的元素,使得不重复元素出现在序列的开始部分,unique的迭代器
指向最后一个不重复元素之后的位置,此位置之后的元素任然存在,但是我们不知道他的值
标准库算法对迭代器而不是容器进行操作,因此算法不能直接添加或删除元素
*/
words.erase(end_unique, words.end());
}
//定制操作
//向算法传递函数-谓词
bool isShorter(const string& s1, const string& s2) {
return s1.size() < s2.size();
}
void test02() {
vector<string>words;
sort(words.begin(), words.end(), isShorter);
}
//lambda
/*
一个lambda表达式表示一个可调用的代码单元,可以理解为未命名的内联函数,一个lambda具有返回类型,一个参数列表,一个函数体
但是与函数不同,lambda可以定义在函数内部
*/
auto f = [] {return 42; };
void test03() {
cout << f() << endl;
auto a = [](const string& a, const string& b) {
return a.size() < b.size();
};
//按照长度排序,长度相同的单词维持字典序
vector<string>words;
stable_sort(words.begin(), words.end(), [](const string& a, const string& b) {return a.size() < b.size(); });
//使用捕获列表
int sz;
[sz](const string& a, const string& b) {return a.size() < b.size(); };
//调用find_if
//获取一个迭代器,指向第一个满足size() >= sz的元素
auto wc = find_if(words.begin(), words.end(), [sz](const string& a, const string& b) {return a.size() < b.size(); });
//for_each算法
for_each(wc, words.end(), [](const string& s) {cout << s << " "; });//捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和他所在函数之外声明的名字
}
//lambda的值捕获和引用捕获
void fun1() {
size_t v1 = 42;
auto f = [v1] {return v1; };
v1 = 0;
auto j = f(); // j为42,f保存我们创建他时v1的拷贝
}
void fun2() {
size_t v1 = 42;
auto f2 = [&v1] {return v1; };
v1 = 0;
auto j = f2(); // j为0
}
//引用捕获有时是必要的,我们可能希望biggies函数接受一个ostream的引用,用来输出数据
vector<string>words;
void biggies(vector<string>& word, vector<string>::size_type sz, ostream& os = cout, char c = ' ') {
for_each(words.begin(), words.end(), [&os, c](const string& s) {os << s << s; });
}
//隐式捕获
int sz;
//sz为隐式捕获,值捕获方式
auto wc = find_if(words.begin(), words.end(), [=](const string& s) {return s.size() >= sz; });
//如果我们希望对一部分变脸采用值捕获,对其他变量采用引用捕获,可以混合使用隐式捕获和显示捕获
void biggies(vector<string>& words, vector<string>::size_type sz, ostream &os = cout, char c = ' ') {
//os隐式捕获,引用捕获方式,c为显示捕获,值捕获方式
for_each(words.begin(), words.end(), [&, c](const string& s) {os << s << c; });
//os显示捕获,引用捕获方式,c隐式捕获,值捕获方式
for_each(words.begin(), words.end(), [=, &os](const string& s) {os << s << s; });
}
//可变的lambda
/*
默认情况下,对于一个值被拷贝的变量,lambda不会改变其值,如果我们希望改变一个被捕获的变量的值,就必须在参数列表上
加上关键词mutable
*/
void fun3() {
size_t v1 = 42;
auto f = [v1]()mutable {return ++v1; };
v1 = 0;
auto j = f();
}
//一个引用捕获的变量是否可以修改依赖于此引用指向的是一个const类型还是非const类型
void fun4() {
size_t v1 = 42;
auto f2 = [&v1] {return ++v1; };
v1 = 0;
auto j = f2(); //j为1;
}
//指定lambda的返回类型
vector<int>vi;
void test05() {
transform(vi.begin(), vi.end(), vi.begin(), [](int i) {return i < 0 ? -i : i; });
//前两个迭代器表示输入序列,第三个迭代器表示目的位置
//但是如果将程序改写为如下的if语句,就会产生编译错误
transform(vi.begin(), vi.end(), vi.begin(), [](int i) {if (i < 0)return -i; else return i; });
//编译器推荐这个版本的lambda返回类型为void,但是他返回了一个int值,但我们需要为一个lambda定义返回类型时,必须使用尾置返回类型
transform(vi.begin(), vi.end(), vi.begin(), [](int i)->int {if (i < 0)return -i; else return i; });
}
//参数绑定
bool check_size(const string& s, string::size_type sz) {
return s.size() >= sz;
}
//标准库bind函数
//绑定check_sizez的sz函数
void test06() {
auto check6 = bind(check_size, placeholders::_1, 6);
string s = "hello";
bool b1 = check6(s);
auto wc = find_if(words.begin(), words.end(), bind(check_size, placeholders::_1, sz));
}
//使用placeholders
using std::placeholders::_1;
/*bind的参数
auto g = bind(f, a, b, _2, c, _1);
f是一个可调用对象它有五个参数,上面的操作将生成一个新的可调用对象,它有两个参数,分别用那两个占位符表示
这个新的可调用对象将自己的参数作为第三个和第五个参数传递给f,f的第一个,第二个,第四个参数分别被绑定在给定的值a,b,c上
g(_1, _2)
映射f(a, b, _2, c, _1)
*/
//使用bind重排参数顺序
static void test07() {
sort(words.begin(), words.end(), isShorter);
sort(words.begin(), words.end(), bind(isShorter, placeholders::_2, _1));
//第一个调用中sort需要比较两个元素a和b时,它会调用isShorter(a, b)
//第二个调用中,传递的参数被交换过来了,因此,sort比较两个元素时,就好像调用isShorter(b, a)
}
//绑定引用参数
void test08(vector<string>& words, vector<string>::size_type sz, ostream& os = cout, char c = ' ') {
for_each(words.begin(), words.end(), [&os, c](const string& s) {os << s << c; });
for_each(words.begin(), words.end(), bind(print, os, _1, ' ')); //错误,不可以拷贝os
/*
原因在bing拷贝其参数,而我们不能拷贝一个ostream,如果我们希望传递给bind一个对象而又不拷贝它,就必须使用标准库ref函数
*/
for_each(words.begin(), words.end(), bind(print, ref(os), _1, ' '));
}
ostream& print(ostream& os, const string& s, char c) {
return os << s << c;
}
//可以代替上面那个函数
//插入迭代器
void test09() {
list<int>lst = { 1, 2, 3, 4 };
list<int>lst2, list<int>lis3;
copy(lst.cbegin(), lst.cend(), front_inserter(lst2));
copy(lst.cbegin(), lst.cend(), inserter(lis3, lis3.begin()));
}
//istream迭代器
//iostream_iterator
void test10() {
vector<int>vec;
istream_iterator<int>int_it(cin);
istream_iterator<int>int_eof;
ifstream in("afile");
istream_iterator<string>str_it(in);
istream_iterator<int>in_iter(cin);
istream_iterator<int>eof;
while (in_iter != eof) {
vec.push_back(*in_iter++);
}
istream_iterator<int>in_iter(cin), eof;
vector<int>vec(in_iter, eof);//从迭代器范围构造vec,这意味着元素范围是从关联的流中读取数据获得的,这个构造函数从cin中
//读取数据,直到遇到文件尾部或者遇到一个不是int的数据为止
//使用算法操作流迭代对象
istream_iterator<int> in(cin), eof;
cout << accumulate(in, eof, 0) << endl;
}
//ostream操作
void test11() {
vector<int>vec;
ostream_iterator<int>out_iter(cout, " ");
for (auto e : vec) {
*out_iter++ = e; //赋值语句实际将元素写到cout
}
cout << endl;
//值得注意的是,当我们向out_iter赋值时,可以忽略解引用和递增运算
for (auto e : vec) {
out iter = e;
}
cout << endl;
//可以调用copy来打印vec中的元素
copy(vec.begin(), vec.end(), out_iter);
}
//反向迭代器
void test12() {
vector<int>vec = { 0, 1, 2, 3, 4, 5, 6, 7 };
for (auto r_iter = vec.crbegin(); r_iter != vec.crend(); ++r_iter)
cout << *r_iter << endl;
sort(vec.begin(), vec.end());
sort(vec.rbegin(), vec.rend());//逆序排序
}
//反向迭代器在书上364页,不想敲了
//区别拷贝元素的版本和不拷贝的版本
void test13() {
vector<int>vec = { 0, 1, 2, 3,4 };
vector<int>dest(10);
auto beg = vec.begin();
auto end = vec.end();
reverse(beg, end);
reverse_copy(beg, end, dest);
vector<int>v1 = { 1, 2, 3 };
vector<int>v2;
remove_if(vec.begin(), vec.end(), [](int i) {return i % 2; });
remove_copy_if(v1.begin(), v1.end(), back_inserter(v2), [](int i) {return i % 2; });
}
//
int main() {
return 0;
}