c++_STL_3_算法(未完)

函数对象


<algorithm>是所有stl头文件中最大的一个。

<numeric>很小,只有几个在序列上进行数学运算的模板函数。

<functional>定义了一些模板类。

算法可以直接修改对象,也可以复制一份。

算法分类:

  • 非可变序列算法:不直接修改容器内容。
    • 计数:count,count_if
    • 搜索:search,find,find_if,find_first_of
    • 比较:equal,mismatch,lexicographical_compare
  • 可变序列算法:可修改容器内容。
    • 删除:remove,remove_if,remove_copy
    • 修改:for_each,transform
    • 排序:sort,stable_sort,partial_sort

函数对象:重载函数调用操作符的类,其对象称为函数对象。通过重载类的operator()实现。

有时可以把它看作函数对待

1. 函数对象做参数和返回值

template <class InputIterator, class Function> Function for_each (InputIterator first, InputIterator last, Function fn);

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<functional>

template <typename T>
class ShowElement{
public:
	int a;
public:
	ShowElement()
	{
		a = 0;
	}
	void operator()(T &t)
	{
		cout<<t<<" ";
		a++;
	}
};

int main(int argc, char *argv[])
{
	vector<int> v;
	ShowElement<int> showElement;
	ShowElement<int> show2;
	
	v.push_back(1);
	v.push_back(3);
	v.push_back(5);
	
	for_each(v.begin(),v.end(),showElement);
	cout<<showElement.a<<endl;
	
	show2 = for_each(v.begin(),v.end(),showElement);
	cout<<show2.a<<endl;
    
    show2 = for_each(v.begin(),v.end(),ShowElement<int>());//匿名函数对象
	cout<<show2.a<<endl;
    
	cin.get();
	return 0;
}

/*
1 3 5 0
1 3 5 3
1 3 5 3
*/

对于第一个for_each(v.begin(),v.end(),showElement),改变的a属于形参,不影响实参的a。也就是说,for_each()的函数对象参数,采用值传递,而不是引用传递,并且返回函数对象。

stl算法返回值有两种:迭代器和函数对象.

函数对象相对于回调函数的好处是,可以保持调用状态信息,即使使用匿名函数对象。

2. 谓词

算法谓词,Predicate,即标准库算法传递的参数。

一元谓词,即一个参数。

谓词的返回值为bool类型,以返回值的结果作为函数的操作条件。为此的形参要与对应容器的类型相同。

template <class InputIterator, class UnaryPredicate> InputIterator find_if (InputIterator first, InputIterator last, UnaryPredicate pred);

第三个参数是一个谓词Predicate,函数返回谓词为真的容器位置的迭代器。

#include<iostream>
using namespace std;

#include<vector>
#include<algorithm>


template <typename T>
class IsDiv
{
public:
	T divisor;
public:
	IsDiv(const T &divisor)
	{
		this->divisor = divisor; 
	}
	bool operator()(T &t)
	{
		return (t % divisor == 0);
	}
};
int main(int argc, char *argv[])
{
	vector<int> v;
	IsDiv<int> div(4);
	for(int i = 10; i < 20; i++)
	{
		v.push_back(i);
	}
	
	vector<int>::iterator it;
	it = find_if(v.begin(),v.end(),div);
	if(it == v.end())
	{
		cout<<"no result."<<endl;
	}
	else
	{
		cout<<*it<<" is divisible by "<<div.divisor<<endl;
	}
	
	cin.get();
	return 0;
}
/*
12 is divisible by 4
*/

二元

#include<iostream>
#include<vector>
using namespace std;

template <typename T>
class SumAdd
{
public:
	T operator()(T t1,T t2)
	{
		return t1 + t2;
	}
};

int main()
{
	vector<int> v1,v2;
	vector<int> v3;
	
	v1.push_back(1);
	v1.push_back(3);
	v1.push_back(5);
	
	v2.push_back(2);
	v2.push_back(4);
	v2.push_back(6);
	
	v3.resize(10);
	
	transform(v1.begin(),v1.end(),v2.begin(),v3.begin(),SumAdd<int>());
	
	for(vector<int>::iterator it = v3.begin(); it != v3.end(); it++)
	{
		cout<<*it<<" ";
	}
	
	return 0;
}

/*
3 7 11 0 0 0 0 0 0 0
*/

transform()文档

二元谓词典型的用法是判断式,比如比较大小。

#include<iostream>
#include<vector> 
using namespace std;

bool MyComp(const int&a,const int &b)
{
	return a < b;
}

int main(int argc, char *argv[])
{
	vector<int> v1;
	
	for(int i = 0; i < 10; i++)
	{
		v1.push_back(rand()%100);
	}
	for(vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
	{
		cout<<*it<<" ";
	}
	cout<<endl;
	
	sort(v1.begin(),v1.end(),MyComp);
	
	for(vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
	{
		cout<<*it<<" ";
	}
	cout<<endl;
	
	system("pause");
	return 0;
}

二元谓词与set

#include<iostream>
#include<set>
#include<algorithm>
using namespace std;

struct CompNoCase
{
	bool operator()(const string &str1, const string &str2) const	//const!!!!!!!!!!!!!!!!!!!!!
	{
		string sTempStr1;
		sTempStr1.resize(str1.size());
		transform(str1.begin(), str1.end(), sTempStr1.begin(), tolower);

		string sTempStr2;
		sTempStr2.resize(str2.size());
		transform(str2.begin(), str2.end(), sTempStr2.begin(), tolower);

		return (sTempStr1 < sTempStr2);
	}
};


int main(int argc, char *argv[])
{
	set<string,CompNoCase> set1;
	set1.insert("aaa");

	set<string,CompNoCase>::iterator it = set1.find("aAa");
	if (it == set1.end())
	{
		cout << "no result" << endl;
	}
	else
	{
		cout << "found" << endl;
	}

	system("pause");
	return 0;
}
/*
found
*/

3. 预定义函数对象和函数适配器

不常用。

<functional>有很多预定义的函数对象,比如plus<>可对不同的数据类型进行加法运算。

#include<iostream>
#include<functional>
#include<string>
using namespace std;


int main(int argc, char *argv[])
{
	plus<string> stringAdd;
	string s1 = "aa";
	string s2 = "bb";
	cout << stringAdd(s1,s2) << endl;

	system("pause");
	return 0;
}
/*
aabb
*/

这样就通过函数对象技术实现了数据结构和算法的分离。

函数适配器

有4类:

  • bind adapter
  • composite adapter
  • pointer function adapter
  • member function adapter

前两种常用。

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<functional>
using namespace std;


int main(int argc, char *argv[])
{
	vector<string> v1;
	v1.push_back("aaa");
	v1.push_back("aaa");
	v1.push_back("bbb");

	string sa = "aaa";

	cout << count_if(v1.begin(), v1.end(), bind2nd(equal_to<string>(),sa)) << endl;

	system("pause");
	return 0;
}
/*
2
*/

bind2nd()函数适配器把预定义函数对象和一个参数进行了绑定。

其它函数适配器辅助函数:bind1st(),not1(),not2().

//calc the number of odd numbers
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<functional>
using namespace std;


int main(int argc, char *argv[])
{
	vector<int> v1;

	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(4);
	v1.push_back(6);

	cout << count_if(v1.begin(), v1.end(), not1(bind2nd(modulus<int>(),2))) << endl;

	system("pause");
	return 0;
}
/*
3
*/

4. stl的容器算法迭代器的设计理念

iterator
iterator
container
algorithm
container
function object

容器通过类模板技术,实现数据类型和容器模型的分离;算法通过函数对象,实现自定义数据类型的算法运算。

函数对象的核心思想:回调函数。

5. 两个修改型算法

for_each()

用指定函数对指定范围内所有元素进行迭代访问,该函数不得修改序列中的元素。

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<functional>
using namespace std;

class CMyShow
{
private:
	int num;
public:
	CMyShow()
	{
		num = 0;
	}
	void operator()(int &n)
	{
		num++;
		cout << n << " ";
	}
	void printNum()
	{
		cout << "num:" << num << endl;
	}
};

int main(int argc, char *argv[])
{
	vector<int> v1;

	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);

	CMyShow m;
	
	CMyShow m1 = for_each(v1.begin(), v1.end(), CMyShow());//init
	m1.printNum();

	m1 = for_each(v1.begin(), v1.end(), m);  //assign
	m1.printNum();

	system("pause");
	return 0;
}

/*
1 2 3 num:3
1 2 3 num:3
*/

transform()

for_each()类似,但可修改容器元素。

当源与目标相同时,则和for_each()一样。

两种形式:

  • OutputIterator transform (InputIterator first1, InputIterator last1, OutputIterator result, UnaryOperation op);
  • OutputIterator transform (InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, OutputIterator result,BinaryOperation binary_op);
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<functional>
#include<list>
#include<iterator>
using namespace std;

int increase(int i)
{
	return i + 1;
}
void printV(vector<int> v)
{
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

int main(int argc, char *argv[])
{
	vector<int> v1;

	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);

	//callback function
	transform(v1.begin(),v1.end(),v1.begin(),increase);
	printV(v1);

	//defined function object
	transform(v1.begin(), v1.end(), v1.begin(), negate<int>());
	printV(v1);

	// function adapter
	list<int> l;
	l.resize(v1.size());
	transform(v1.begin(),v1.end(),l.begin(),bind2nd(multiplies<int>(),10));

	list<int>::iterator it = l.begin();
	while (it != l.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	

	//output directly
	transform(v1.begin(), v1.end(),  ostream_iterator<int>(cout," "), negate<int>());


	system("pause");
	return 0;
}
/*
2 3 4
-2 -3 -4
-20 -30 -40
2 3 4
*/

两者比较

两者对函数对象的要求不同。

for_each()的函数对象,参数是引用,可以没有返回值,transform()的函数对象,参数不是引用,必须有返回值。可调试分析源码。

for_each()速度快,不灵活;transform()速度慢,更灵活。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章