泛型算法Generic Algorithm
大多数算法都定义在头文件#include <algorithm>
和头文件#include <numeric>
中。一般情况下这些算法并不直接操作容器,而是遍历由两个迭代器指定的一个元素范围来进行操作。
1. 只读算法std::find()
查找、std::count()
计数
std::find()
原型:
template <class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val);
std::count()
原型:
template <class InputIterator, class T>
typename iterator_traits<InputIterator>::difference_type
count (InputIterator first, InputIterator last, const T& val);
std::find()
返回指向第一个等于给定值的元素的迭代器,如果范围中无匹配的元素,则std::find()
返回第二个参数来表示搜索失败。
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
int main()
{
auto alist = { 1, 2, 3, 4 ,3}; // 类型是:class std::initializer_list<int>
const int* it = std::find(std::begin(alist), std::end(alist), 3);
std::cout << (it != std::end(alist) ? "find 3" : "not find 3")<<std::endl;
int num = std::count(std::begin(alist), std::end(alist), 3);
std::cout << "3 appears " << num << " times" << std::endl;
int myints[] = { 10, 20, 30, 30, 20, 10, 10, 20 };
std::vector<int> vec(myints, myints + 8);
std::cout << "20 appears " << std::count(vec.cbegin(), vec.cend(), 20) <<" times"<< std::endl;
std::list<std::string> lst({ "This", "is", "a", "string", "value." });
std::string tmpstr("string");
std::cout << (std::find(lst.cbegin(), lst.cend(), tmpstr) != lst.cend() ? "find tmpstr" : "not find tmpstr") << std::endl;
system("pause");
}
输出结果是:
find 3
3 appears 2 times
20 appears 3 times
find tmpstr
2. 只读算法std::accumulate()
求和
常用形式:
template <class InputIterator, class T>
T accumulate (InputIterator first, InputIterator last, T init);
自定义形式:
template <class InputIterator, class T, class BinaryOperation>
T accumulate (InputIterator first, InputIterator last, T init,
BinaryOperation binary_op);
其中init作为binary_op()的第一个参数,first与last直接元素的和作为binary_op()的第二个参数。
常用形式的例子:
#include <iostream>
#include <numeric>
#include <vector>
int main()
{
int myints[] = { 10, 20, 30, 30, 20, 10, 10, 20 };
std::vector<int> vec(myints, myints + 8);
int sum = std::accumulate(vec.cbegin(), vec.cend(), 10);
std::cout << "sum of myints is: " << sum << std::endl;
system("pause");
}
输出结果是sum of myints is: 160
。
自定义形式的例子:
// accumulate example
#include <iostream> // std::cout
#include <numeric> // std::accumulate
int myfunction(int x, int y) { return x + 2 * y; }
struct myclass {
int operator()(int x, int y) { return x + 3 * y; }
} myobject;
int main() {
int init = 1;
int numbers[] = { 1, 2, 3 };
std::cout << "using custom function: ";
std::cout << std::accumulate(numbers, numbers + 3, init, myfunction) << std::endl;
std::cout << "using custom object: ";
std::cout << std::accumulate(numbers, numbers + 3, init, myobject) << std::endl;
system("pause");
}
输出结果是:
using custom function: 13
using custom object: 19
也可以用来连接std::string()
:
#include <iostream>
#include <numeric>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> vec({"This ","is ","a ","vector ","of ","string","."});
std::string concat_str = std::accumulate(vec.cbegin(), vec.cend(), std::string(""));
std::cout << concat_str << std::endl;
system("pause");
}
输出的结果是:This is a vector of string.
。
注意,std::string("")
不能写成""
,因为""
是字面值其类型是const char*
,而const char*
是不能进行+
操作的。
3. 只读算法std::equal()
确定两个序列是否保存相同的值
std::equal()
关心的是序列的元素是否相同,即元素间是可比较的,例如double
和int
,std::string
和const char*
;也不关心序列是否是相同类型,例如可比较std:vector<int>
和int []
。
需要注意的是第二个序列长度要大于等于第一个序列。
常用形式:
template <class InputIterator1, class InputIterator2>
bool equal (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2);
自定义形式:
template <class InputIterator1, class InputIterator2, class BinaryPredicate>
bool equal (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, BinaryPredicate pred);
参数说明:first1
、last1
表示序列1的范围,first2
表示序列2的首元素。
// equal algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::equal
#include <vector> // std::vector
bool mypredicate(int i, int j) {
return (i == abs(j)||j == abs(i));
}
int main() {
int myints[] = { 20, 40, 60, 80, 100 }; // myints: 20 40 60 80 100
std::vector<int>myvector(myints, myints + 5); // myvector: 20 40 60 80 100
// using default comparison:
if (std::equal(myvector.begin(), myvector.end(), myints))
std::cout << "序列完全相同.\n";
else
std::cout << "序列不完全相同.\n";
myvector[3] = -80; // myvector: 20 40 60 -80 100
// using predicate comparison:
if (std::equal(myvector.begin(), myvector.end(), myints, mypredicate))
std::cout << "序列的绝对值完全相同.\n";
else
std::cout << "序列的绝对值不完全相同.\n";
}
输出的结果是:
序列完全相同.
序列的绝对值完全相同.
4. 写算法std::fill()
、std::fill_n()
std::fill()
和std::fill_n()
执行的是元素操作,不会执行容器操作,因此不会改变容器的大小。 std::fill()
原型:
template <class ForwardIterator, class T>
void fill (ForwardIterator first, ForwardIterator last, const T& val);
std::fill_n()
原型:
template <class OutputIterator, class Size, class T>
void fill_n (OutputIterator first, Size n, const T& val);
例子:
#include <iostream> // std::cout
#include <algorithm> // std::fill_n\std::fill
#include <vector> // std::vector
int main() {
std::vector<int> vec;
vec.resize(10);
std::fill(vec.begin(), vec.end(), 3);
for (auto it:vec)
{
std::cout << it << " ";
}
std::cout << std::endl;
std::fill_n(vec.begin() + 2, 5, 6);
for (auto it : vec)
{
std::cout << it << " ";
}
std::cout << std::endl;
system("pause");
}
输出结果:
3 3 3 3 3 3 3 3 3 3
3 3 6 6 6 6 6 3 3 3
5. 插入迭代器std::back_inserter()
std::back_inserter()
原型:
template <class Container>
back_insert_iterator<Container> back_inserter (Container& x);
std::back_inserter()
接受一个指向容器的引用,返回一个与该容器绑定的插入迭代器。当通过此迭代器赋值时,赋值运算符会调用push_back
将一个具有给定值的元素添加到容器中。
举例说明:
#include <iostream> // std::cout
#include <algorithm> // std::fill_n\std::fill
#include <vector> // std::vector
#include <iterator> // std::back_inserter
int main() {
std::vector<int> vec;
std::fill_n(std::back_inserter(vec), 5, 3);
for (auto it = vec.cbegin(); it != vec.cend(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
输出结果:3 3 3 3 3
。
6. 拷贝算法std::copy()
std::copy()
原型:
template <class InputIterator, class OutputIterator>
OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result);
举例说明:
#include <iostream> // std::cout
#include <algorithm> // std::fill_n\std::fill
#include <vector> // std::vector
#include <iterator> // std::back_inserter
#include <initializer_list> //std::initializer_list
int main() {
std::vector<int> vec;
std::initializer_list<int> lst({ 5, 4, 3, 2, 1 });
std::copy(lst.begin(), lst.end(), std::back_inserter(vec));
for (auto it = vec.cbegin(); it != vec.cend(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
}
7. 写算法std::replace()
、std::replace_copy()
函数原型:
template <class ForwardIterator, class T>
void replace (ForwardIterator first, ForwardIterator last,
const T& old_value, const T& new_value);
template <class InputIterator, class OutputIterator, class T>
OutputIterator replace_copy (InputIterator first, InputIterator last,
OutputIterator result,
const T& old_value, const T& new_value);
std::replace()
读入一个序列,并将其所有等于给定值old_value
的元素都改为另一个值new_value
。此算法接受4个参数,前两个时迭代器,表示输入序列,后两个一个时要搜索的值,另一个时新值。
如果希望保留原序列不变,可以调用std::replace_copy()
,此算法接受第三个迭代器表示调整后序列的保存位置。
举例说明:
#include <iostream> // std::cout
#include <algorithm> // std::fill_n\std::fill
#include <vector> // std::vector
#include <iterator> // std::back_inserter
#include <initializer_list> //std::initializer_list
int main() {
std::vector<int> vec({ 5, 4, 3, 2, 1 });
std::replace(vec.begin(), vec.end(), 3, 0);
std::cout << "vec's value: ";
for (auto it = vec.cbegin(); it != vec.cend(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
std::vector<int> vec_new;
/* vec保持不变,新的结果保存在vec_new中 */
std::replace_copy(vec.cbegin(), vec.cend(), std::back_inserter(vec_new), 5, 6);
std::cout << "vec_new's value: ";
for (auto it = vec_new.cbegin(); it != vec_new.cend(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
std::cout << "vec's value: ";
for (auto it = vec.cbegin(); it != vec.cend(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
system("pause");
}
输出结果:
vec's value: 5 4 0 2 1
vec_new's value: 6 4 0 2 1
vec's value: 5 4 0 2 1
8. 写算法std::unique()
std::unique
算法重排输入序列将相邻的重复项“消除”,并返回一个指向不重复值范围末尾的迭代器。
原型为:
equality (1)
template <class ForwardIterator>
ForwardIterator unique (ForwardIterator first, ForwardIterator last);
predicate (2)
template <class ForwardIterator, class BinaryPredicate>
ForwardIterator unique (ForwardIterator first, ForwardIterator last,
BinaryPredicate pred);
举例说明:
// unique algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::unique, std::distance
#include <vector> // std::vector
bool myfunction(int i, int j) {
return (i == j);
}
int main() {
int myints[] = { 10, 20, 20, 20, 30, 30, 20, 20, 10 }; // 10 20 20 20 30 30 20 20 10
std::vector<int> myvector(myints, myints + 9);
// using default comparison:
std::vector<int>::iterator it;
it = std::unique(myvector.begin(), myvector.end()); // 10 20 30 20 10 ? ? ? ?
/* 下面这句也可替换为:myvector.erase(it, myvector.end()); */
myvector.resize(std::distance(myvector.begin(), it)); // 10 20 30 20 10
// using predicate comparison:
std::unique(myvector.begin(), myvector.end(), myfunction); // (no changes)
// print out content:
std::cout << "myvector contains:";
for (it = myvector.begin(); it != myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
}
输出结果为:myvector contains: 10 20 30 20 10
。
如果想删除所有重复的元素,则可先对元素数据进行排序sort,然后再使用unique,例如:
#include <iostream> // std::cout
#include <algorithm> // std::unique, std::distance
#include <vector> // std::vector
int main() {
int myints[] = { 10, 20, 20, 20, 30, 30, 20, 20, 10 };
std::vector<int> myvector(myints, myints + 9);
std::sort(myvector.begin(), myvector.end());
std::vector<int>::iterator it;
it = std::unique(myvector.begin(), myvector.end());
myvector.resize(std::distance(myvector.begin(), it));
std::cout << "myvector contains:";
for (it = myvector.begin(); it != myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
system("pause");
return 0;
}
输出结果为:myvector contains: 10 20 30
。
9. 只读算法std::distance()
确定两个迭代器直接的举例
函数原型:
template<class InputIterator>
typename iterator_traits<InputIterator>::difference_type
distance (InputIterator first, InputIterator last);
举例说明:
// advance example
#include <iostream> // std::cout
#include <iterator> // std::distance
#include <list> // std::list
int main () {
std::list<int> mylist;
for (int i=0; i<10; i++) mylist.push_back (i*10);
std::list<int>::iterator first = mylist.begin();
std::list<int>::iterator last = mylist.end();
std::cout << "The distance is: " << std::distance(first,last) << '\n';
}
输出结果为:The distance is: 10
。
10. 排序算法std::sort()
、std::stable_sort
std::sort()
原型:
default (1)
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
custom (2)
template <class RandomAccessIterator, class Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
std::stable_sort
原型:
template <class RandomAccessIterator>
void stable_sort ( RandomAccessIterator first, RandomAccessIterator last );
template <class RandomAccessIterator, class Compare>
void stable_sort ( RandomAccessIterator first, RandomAccessIterator last,
Compare comp );
std::stable_sort
用法与std::sort
相同,区别时std::stable_sort
是稳定的排序算法,维持相等元素的原有顺序。
举例说明:
// sort algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::sort
#include <vector> // std::vector
bool myfunction (int i,int j) { return (i<j); }
struct myclass {
bool operator() (int i,int j) { return (i<j);}
} myobject;
int main () {
int myints[] = {32,71,12,45,26,80,53,33};
std::vector<int> myvector (myints, myints+8); // 32 71 12 45 26 80 53 33
// using default comparison (operator <):
std::sort (myvector.begin(), myvector.begin()+4); //(12 32 45 71)26 80 53 33
// using function as comp
std::sort (myvector.begin()+4, myvector.end(), myfunction); // 12 32 45 71(26 33 53 80)
// using object as comp
std::sort (myvector.begin(), myvector.end(), myobject); //(12 26 32 33 45 53 71 80)
// print out content:
std::cout << "myvector contains:";
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
}
输出结果:myvector contains: 12 26 32 33 45 53 71 80
。
#include <iostream> // std::cout
#include <algorithm> // std::stable_sort
#include <vector> // std::vector
#include <string>
struct mystruct{
bool operator()(std::string i, std::string j){ return i.size() < j.size(); }
};
int main() {
std::vector<std::string> vec({ "the", "quick", "red", "fox", "jumps",
"over", "the", "slow", "red", "turtle" });
mystruct myobj;
std::stable_sort(vec.begin(), vec.end(), myobj);
for (auto it : vec)
{
std::cout << it << " ";
}
std::cout << '\n';
}
输出结果为:the red fox the red over slow quick jumps turtle
。
11. std::transform()
函数原型:
unary operation(1)
template <class InputIterator, class OutputIterator, class UnaryOperation>
OutputIterator transform (InputIterator first1, InputIterator last1,
OutputIterator result, UnaryOperation op);
binary operation(2)
template <class InputIterator1, class InputIterator2,
class OutputIterator, class BinaryOperation>
OutputIterator transform (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, OutputIterator result,
BinaryOperation binary_op);
举例说明:
// transform algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::transform
#include <vector> // std::vector
#include <functional> // std::plus
int op_increase(int i) { return ++i; }
int main() {
std::vector<int> foo;
std::vector<int> bar;
// set some values:
for (int i = 1; i < 6; i++)
foo.push_back(i * 10); // foo: 10 20 30 40 50
bar.resize(foo.size()); // allocate space
std::transform(foo.begin(), foo.end(), bar.begin(), op_increase);
// bar: 11 21 31 41 51
// std::plus adds together its two arguments:
std::transform(foo.begin(), foo.end(), bar.begin(), foo.begin(), std::plus<int>());
// foo: 21 41 61 81 101
}
12. lambda表达式
lambda表达式形式: [capture list] (parameter list) -> return type {function body}
labmda的函数参数可以时普通类型也可以时引用,例如:
// for_each example
#include <iostream> // std::cout
#include <algorithm> // std::for_each
#include <vector> // std::vector
int main() {
std::vector<int> myvector({10,20,30});
/* myvector的迭代器解引用后赋给i,由于i是引用,因此会改变myvector中元素的值 */
std::for_each(myvector.begin(), myvector.end(), [](int &i){++i;});
for (auto it : myvector)
{
std::cout << it << " ";
}
std::cout << '\n';
}
输出结果是11 21 31
。
值捕获
值捕获,被捕获变量的值是在lambda创建时拷贝,因此随后对变量进行修改不会影响到lambda内对于的值。举例说明:
#include <iostream>
int main() {
int val(90); // 局部变量
/* 将val拷贝到名为fun的可调用对象 */
auto fun = [val]{return val; };
val = 10; // 值捕获,对变量进行修改不会影响到lambda内对于的值
/* 输出90,fun中保持了创建fun时val的拷贝 */
std::cout << fun() << std::endl;
}
引用捕获
一个以引用方式捕获的变量与其它任何类型的引用行为类似。当在lambda函数体内使用此变量时,实际上使用的时引用所绑定的对象。
#include <iostream>
int main() {
int val(90); // 局部变量
/* 对象fun包含val的引用 */
auto fun = [&val]{return val; };
val = 10;
/* 输出10,fun保存val的引用,而非拷贝 */
std::cout << fun() << std::endl;
}
隐式捕获
除了显示列出希望使用的来自所在函数的变量之外,还可以让编译器根据lambda体中的代码来推断要使用哪些变量。为了指示编译器推断捕获列表,应该在捕获列表中写出=(值捕获)或&(引用捕获)。
隐式值捕获:
#include <iostream>
int main() {
int val(90);
/* =表示隐式值捕获 */
auto fun = [=]{return val; };
val = 10;
/* 输出90 */
std::cout << fun() << std::endl;
}
隐式引用捕获:
#include <iostream>
int main() {
int val(90);
/* &表示隐式引用捕获 */
auto fun = [&]{return val; };
val = 10;
/* 输出10 */
std::cout << fun() << std::endl;
}
混合使用隐式捕获和显示捕获
#include <iostream>
int main() {
int val1(80);
int val2(90);
auto fun = [&,val2]{
val1++;
//val2++; // 值引用,在这里时右值因此此句无法编译通过;可通过mutable改变其值,见后文
return (val1 + val2); };
std::cout << val1 << std::endl; // 输出80
std::cout << fun() << std::endl; // 输出171
std::cout << val1 << std::endl; // 输出81
}
捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和它所在函数之外声明的名字。
可变lambda
默认情况下,对于一个值被拷贝的变量,lambda不会改变其值,如果希望改变一个被捕获的变量的值,可以使用关键字mutable或者使用引用。
举例说明:
#include <iostream>
int main() {
int val1(80);
int val2(90);
auto fun = [&, val2]()mutable{
val1++;
/* val2是值引用,通过mutable来使其可以在lambda函数内部被修改,
但是由于其是值引用所以这种改变是对局部变量的改变,不会修改
lambda外部的val2的值 */
val2++; // 如果不加mutable此句是无法编译通过的
return (val1 + val2); };
std::cout << val1 << std::endl; // 输出80
std::cout << fun() << std::endl; // 输出172
std::cout << val1 << std::endl; // 输出81
std::cout << val2 << std::endl; // 输出90
}
从上面例子可看出对于值捕获,如果想在lambda函数体中修改值则必须加mutable,对捕获的值的修改影响范围只在lambda体内。而通过引用捕获在lambda函数体内对值进行修改时,对捕获的值的修改是会影响到lambda函数体外的。
lambda返回类型
lambda是一中可以写在其它函数体内部的函数,因此lambda也是由返回值的,如果不写返回值则默认为void。如果一个lambda体包含多条return语句,则多条return语句的返回类型必须相同,否则无法通过编译,例如可以都是int,但如果一个是int,一个是float是无法通过编译的。此时可以通过尾置返回类型来显示指定lambda的返回类型。例如:
#include <iostream> // std::cout
#include <algorithm> // std::transform
#include <vector> // std::vector
int main() {
std::vector<int> foo({ -2, 0, 1 });
/* 使用尾置返回类型显示指定lambda的返回类型 */
std::transform(foo.begin(), foo.end(), foo.begin(), [](int i)->int{
if (i < 0){
return -i;
}
else
{
return i*6.6; // 如果没有尾置返回类型int,此句无法通过编译
}});
for (auto it : foo)
{
std::cout << it << " ";
}
}
输出结果:2 0 6
。
13. 标准库bind
函数
调用bind
的一般形式为: auto newCallable = bind(callable, arg_list)
其中,newCallable
本身是一个可调用对象,arg_list
是一个逗号分割的参数列表,对应给定的callable
的参数。即,当我们调用newCallable
时,newCallable
会调用callable
,并传递给它arg_list
中的参数。
举例说明:
#include <iostream> // std::cout
#include <functional> // std::bind
#include <vector> // std::vector
/* calculate第四个参数是引用 */
int calculate(int i, int j, int k,int &w){
return (i + j)*k+w++;
}
int main() {
std::vector<int> foo({ -2, 0, 1 ,3});
/* bind第四个参数是引用ref */
auto bfun = std::bind(calculate,std::placeholders::_3, std::placeholders::_1, std::placeholders::_2,std::ref(foo.at(3)));
std::cout << bfun(foo.at(0), foo.at(1), foo.at(2)) << std::endl;
for (auto it : foo)
{
std::cout << it << " ";
}
std::cout << '\n';
}
输出结果是:
3
-2 0 1 4
默认情况下,bind
的那些不是占位符的参数被拷贝到bind
返回的可调用对象中。
因此,需要注意的是想要实现在bind
的时候完成对vector foo
第四个值的改变必须在bind
函数和calculate
函数中都进行引用实现。我们来分析下只对一个函数的参数进行引用表示会有什么结果。
假设仅对calculate()
函数的第四个参数进行引用实现而不对bind
的第四个参数进行引用实现,那么在调用bfun
的时候我们其实是将foo.at(3)
的值拷贝给了calculate()
,也就是说在calculate()
中是对foo.at(3)
副本的引用而不是对foo.at(3)
的引用,因此无法改变foo.at(3)
本身。
同样的假设仅对bind
的第四个参数实现引用,而不对calculate()
的第四个参数实现引用,在调用bfun
的时候我们虽然是将foo.at(3)
的引用传递给了bfun
,但是传递给calculate()
的是foo.at(3)
的引用的副本,仍然不是foo.at(3)
本身,所以还是无法实现对foo.at(3)
本身的修改。
综上,要想实现对foo.at(3)
本身的修改必须在calculate()
和bind
同时使用引用。
对于占位符参数(palceholders)是直接使用的,例如:
#include <iostream> // std::cout
#include <functional> // std::bind
#include <vector> // std::vector
#include <algorithm> // std::for_each
void calculate(int &i){
i *= i;
}
using namespace std::placeholders;
int main() {
std::vector<int> vec({ -2, 0, 1, 3 });
/* vec的每个迭代器是直接给到函数calculate的,没有拷贝,
因此只需在calculate中实现函数的引用即可完成对vec中元素的修改 */
std::for_each(vec.begin(), vec.end(),std::bind(calculate,_1));
for (auto it : vec)
{
std::cout << it << " ";
}
std::cout << '\n';
}
输出结果是:4 0 1 9
。
14. 迭代器
插入迭代器(insert iterator)
back_inserter()
front_inserter()
inserter()
流迭代器(stream iterator)
后续补充……