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()速度慢,更靈活。

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