C++ Primer 第十章 泛型算法 筆記
C++ Primer 第十章 泛型算法 練習題
10.1 概述
叠代器令算法不依賴於容器,但算法依賴於元素類型的操作。
10.1
vector<int>vi;
int a;
while(cin>>a)
vi.push_back(a);
auto result=count(vi.cbegin(),vi.cend(),1);
cout<<result;
10.2
改寫類型即可。
10.2 初識泛型算法
除少數例外,標準庫算法都對一個範圍內(第一個元素和尾後元素的叠代器)的元素進行操作。大多數算法遍歷輸入範圍方式相似,但它們使用範圍中元素的方式不同。要了解它們是否讀取元素、改變元素或是重排元素順序。
10.2.1 只讀算法
一些算法只會讀取其輸入範圍內的元素,而不改變元素。
- find,count都是
accumulate (定義在頭文件numeric中),第三個參數是和的初值。
string sum=accumulate(v.cbegin(),v.cend(),string("")) // 將string元素連接
equal 第三個元素接受第二個序列的首元素。這種只接受單一叠代器表示第二個序列的算法,都假定第二個序列至少和第一個序列一樣長。
10.3vector<int>vi{1,2,3}; auto result=accumulate(vi.cbegin(),vi.cend(),0);
10.4
第三個參數決定函數中使用哪個加法運算符和返回值類型。將初值設定為0,表明返回值為int類型,使用之後,會將double轉換為int,損失精度
10.5
經驗證,一切正常,但是C風格字符串的比較最好還是利用strcmp().10.2.2 寫容器元素的算法
一些算法將新值賦予序列中的元素,使用時必須確保序列大小至少不小於我們要求算法寫入的元素數目。
vector<int>vec; fill_n(back_inserter(vec),10,0); //添加10個元素到vec replace_copy(ilst.cbegin(),ilst.cend(),back_inserter(ivec),0,42); //ilst沒變,ivec包含ivec的拷貝,且其中0被替換成了42
10.6
vector<int>vi{1,2,3}; fill_n(vi.begin(),3,0);
10.7
a. lst和vec之間的大小未保證相同.
b. reserve只預留了空間,該容器內還是沒有元素。
10.8 這只是產生了一個插入叠代器,然後使用這個叠代器進行插入操作。並非算法本身改變大小。10.2.3 重排容器元素的算法
消除重復單詞
sort(words.begin(),words.end());//按字典序排序,默認升序 auto end_unique=unique(words.begin(),words.end());//返回指向不重復元素末尾的叠代器 words.erase(end_unique,words.end());
10.9
#include
#include
#include
#include
#include
using namespace std;
void elimDups(vector&s)
{
cout<<"排序前:";
for (const auto &a:s)
{
cout<<a<<" ";
}
cout<<endl<<"sort()排序後:";
sort(s.begin(),s.end());//sort排序
for (const auto &a:s)
{
cout<<a<<" ";
}
cout<<endl<<"unique()排序後:";
auto str = unique(s.begin(),s.end());//unique排序
for (const auto &a:s)
{
cout<<a<<" ";
}
cout<<endl<<"erase()操作後:";
s.erase(str,s.end());//erase()操作
for (const auto &a:s)
{
cout<<a<<" ";
}}
int main(int argc, char**argv)
{
string a[9] = {"I","Like","Like","C++","very","very","much","SCUEC","NewThread"};
vectors(a,a+9);
elimDups(s);
return 0;}
10.10 因為算法始終作用於叠代器,不會直接對容器操作。
10.3 定制操作
10.3.1 向算法傳遞函數
謂詞:是一個可調用的表達式,其返回結果是一個能用做條件的值。元素類型必須能轉換為謂詞的參數類型。
bool isShorter(const string &s1,const string &s2)
{
return s1.size()<s2.size();
}
sort(words.begin(),words.end(),isShorter);//按長度排序
stable_sort:可保持等長元素間的原有順序
10.11 替換10.9例題參數即可。
10.12 編寫謂詞即可。
bool compareIsbn(Sales_data s1, Sales_data s2)
{
return s1.isbn().size() < s2.isbn().size();
}
10.13
bool comp(string &s1)
{return (s1.size() >= 5)}
bool func(string &s1)
{
auto it1=vec.begin();
auto it2=partition(vec.begin(),vec.end(),comp);
if(it1==it2) //若沒有符合要求的,則此時返回的叠代器指向首部
return false;
else
{
for(;it1!=it2;++it1)
cout<<*it1<<" ";
return true;
}
}
10.3.2 lambda表達式
介紹lambda
- 一個lambda表達式表示一個可調用的代碼單元,可理解成一個未命名的內聯函數。與函數不同的是,lambda可能定義在函數內部。
- [捕獲列表](參數列表)->返回類型{函數體}
捕獲列表是一個lambda所在函數中定義的局部變量的列表,通常為空 - lambda必須使用尾置返回
可忽略參數列表和返回類型,但永遠包含捕獲列表和函數體
auto f = [] {return 42;}; //定義可調用對象f,它不接受參數,返回42 cout << f() << endl; //打印42
向lambda傳遞參數
lambda不能有默認參數,因此,一個lambda調用的實參數目永遠和實參數目相等。
stable_sort(words.begin(),words.end(), [](const string &a,const string &b) {return a.size()<b.size();});
使用捕獲列表
/查找第一個長度大於sz的元素/
auto wc=find_if(words.begin(),words.end(),
sz
{return a.size()>sz;});//返回叠代器for_each算法
- lambda可直接調用當前函數之外的名字
for_each算法接受一個可調用對象,並對輸入序列中每個元素調用此對象。
for_each(wc,words.end(), [](const string &s) {cout<<s<<" ";});
10.14
[](int &a,int &b){cout<<a+b;};
10.15
[value](int a){return (value+a);};
10.16 參考示例biggies。
10.17
stable_sort(vec1.begin(),vec1.end(),
[](Sales_data s1, Sales_data s2)
{return s1.isbn().size() < s2.isbn().size();});
10.18
void biggies(vector<string> &s, vector<string>::size_type sz)
{
elimDups(s);//字典排序、刪除重復
stable_sort(s.begin(),s.end(),
[](const string &a,const string &b)
{return a.size()<b.size();});//按長度排序
auto it1 = partition(s.begin(),s.end(),
[sz](const string &s){return s.size()<=sz;});
for (it1; it1 != s.end(); ++it1)
{
cout<<*it1<<" ";
}
}
10.19 換成stable_partition 即可
10.3.3 lambda捕獲和返回
被捕獲變量值是在lambda創建時拷貝,因此其後對其修改不會影響到lambda內對應的值。
引用捕獲
當用引用捕獲時修改值就能影響到lambda內對應值啦。但必須確保被引用對象在lambda執行時存在。
void fun(ostream &os=cout,char c=' ') { for_each(xxx,xxx, [&os,c](const string &s) {os<<s<<C;}); }
隱式捕獲
- 在捕獲列表中寫一個&或=。
當混合使用顯隱時,第一個必須是&或=,且方式不同(比如隱式用了引用,那麽顯示就得用傳值)
可變lambda
如果希望改變一個被捕獲的變量值,就必須在參數列表首加上關鍵字mutable,因此,可變lambda能省略參數列表。
auto f = [v1]() mutable {return ++v1};
指定lambda返回類型
當需要為一個lambda定義返回類型時,要用尾置返回類型!
transform(v.begin(),v.end(),v.begin(), [](int i)->int {if(i<0) return -i;else return i;};
10.20
count_if(vi.begin(),vi.end(), [](const string &a) {a.size()>6;})
10.21
int v=42;
auto f = &v->bool{while (v>0) --v; return !v; } ;標準庫bind函數
- 那種一兩個地方需要用的簡單操作,lambda表達式最好用,否則還是定義一個函數
bind定義在functional中,可將其看作一個函數適配器。
auto New可調用對象 =bind(調用對象,參數列表)
其中參數列表中可用占位符_n,表示占用第n個參數。定義在placeholders命名空間bool check_size(const string &s,string::size_type sz) {return s.size()>=sz;} auto wc=find_if(words.begin(),words.end(), bind(check_size,_1,sz); bool b1=wc("hello"); //用定值6和占位的const string&調用check_size函數
可用bind重新安排對象中參數位置
auto g=bind(f,a,b,_2,c,_1); g(x,y);//即f(a,b,y,c,x) sort(xx,xx,bind(isShorter,_2,_1));//變成由長到短排序
若希望傳遞給bind一個對象而又不拷貝他,可用ref函數
ostream &print(ostream &os,const string &s,char c) {return os<<s<<c}; for_each(xx,xx,bind(print,ref(os),_1,' '));//傳遞ostream
10.22
bool func(string &s,string::size()_type sz) { return s.size()<=sz; } count_if(vec1.begin(),vec1.end(),bind(func,_1,6));
10.23 可調用對象形參數+1(自己)
10.24bool check_size(const string &s,string::size_type sz) {return s.size()>sz;} string::size_type length=xxstring.size(); auto wc=find_if(vi.begin(),vi.end(), bind(check_size,_1,length);
10.25
auto it1 = partition(s.begin(),s.end(), bind(check_size,_1,sz));
10.4 再探叠代器
插入叠代器
插入器是一種適配器,接受一個容器,生成一個叠代器,能實現向給定容器添加元素。
list<int> lst={1,2,3,4}; list<int> lst2,lst3; copy(lst.cbegin(),lst.cend(),front_inserter(lst2);//4321 copy(lst.cbegin(),lst.cend(),inserter(lst3,lst3.begin());//1234
10.26
back_inserter:使用push_back;
front_inserter:使用push_front,每次總把元素插在首位置
inserter:接受第二個參數,參數是指向容器插入位置的叠代器。插在給定元素前面
10.27
sort(vec1.begin(),vec1.end());
unique_copy(vec1.cbegin(),vec1.cend(),back_inserter(vec2));
10.28 見上述示例代碼。註意vector不支持push_front操作。
iostream叠代器
istream_iterator<int> in_iter(cin),eof;
vector<int> vec(in_iter,eof);//叠代器範圍構造
vec.push_back(*in_iter++);//後置遞增構造
istream_iterator<int> in(cin),eof;
cout<< accumulate(in,eof,0) <<endl;//使用算法操作六叠代器
ostream_iterator<int> out_iter(cout," ");
for(auto e:vec)
*out_iter++=e;//循環輸出
copy(vec.begin(),vec.end(),out_iter);//更簡單的循環輸出
10.29
ifstream in1("1.txt");
istream_iterator<string> str(in1),end;
copy(str,end,back_inserter(vec1));
10.30
istream_iterator<int> str(in1),end;
ostream_iterator<int> out_iter(cout," ");
copy(str,end,back_inserter(vec1));
sort(vec1.begin(),vec1.end());
copy(vec1.begin(),vec1.end(),out_iter);
10.31
unique_copy(str,end,back_inserter(vec1));//記得先sort
10.32 我用的find_if。
istream_iterator<Sales_item> item_iter(cin),eof;
vector<Sales_item> vs(item_iter,eof);
sort( vs.begin(), vs.end(), compareIsbn );
auto currt=vs.begin();
auto temp=currt;
while(currt!=vs.end())
{
temp=find_if(vs.begin(),vs.end(),
[currt.isbn()](Sales_ietm b) {return (currt.isbn()!=b.isbn());});
cout<< acumulate(currt,temp,Sales_item())<< endl;
currt=temp;
}
10.33
ifstream in("1.txt");
istream_iterator<int> it1(in),end;
vector<int> vec1;
copy(it1,end,back_inserter(vec1));
ofstream out("2.txt");
ofstream out2("3.txt");
ostream_iterator<int> it2(out," ");
ostream_iterator<int> it3(out2,"\n");
for (auto it=vec1.begin();it!=vec1.end();++it)
{
if ((*it)%2 == 0)//偶數
it2=it;
else
it3 = it;
}
10.4.3 反向叠代器
- 反向叠代器就是在容器中從尾元素向首元素反向移動的叠代器。++會移動到前面,--會移動到下一個元素。
- 除了forward_list之外,都支持反向叠代器。可用rbegin,rend等使用。
- 可用a.base()將其轉換為普通叠代器,輸出需求內容。(否則輸出的是反的)
普通叠代器和反向叠代器的關系反映了左閉合區間。
10.34for (auto it1 = v1.rbegin(); it1 != v1.rend() ; ++it1) cout<<*it1<<" ";
10.35
for (auto it1 = v1.end(); it1 != v1.begin() ;--it1) cout<<*(it1-1)<<" ";
10.36
find(list1.rbegin(),list1.rend(),0);
10.37
copy(vec1.rbegin()+3,vec1.rend()-2,back_inserter(list1));
10.5 泛型算法結構
10.5.1 5類叠代器
10.38
輸入叠代器:可以讀取序列中的元素,只用於順序訪問,*it++必須有效
輸出叠代器,看作是輸入叠代器的補集,可寫,只賦值一次
前向叠代器:單向,支持輸入輸出,只沿一個方向移動
雙向叠代器:雙向,支持讀寫,還支持遞增遞減運算符。
隨機訪問叠代器:基本支持所有功能。
10.39
list:雙向叠代器。
vector:隨機訪問叠代器
10.40
copy的三個參數,第一二個數輸入叠代器,第三個是輸出叠代器
reverse:雙向叠代器
unique是前向叠代器
10.5.2 算法形參模式
10.41
(a):遍歷beg到end,找到oldVal就用newVal替換
(b):遍歷beg到end,找到滿足pred條件的就用newVal替換
(c):遍歷beg到end,找到oldVal就用newVal替換,並將其拷貝至dest
(d):遍歷beg到end,找到滿足pred條件的就用newVal替換,並將其拷貝至dest
10.6 特定容器算法
- 鏈表類型list\forward_list定義了獨有的sort\merge\remove\reverse\unique操作。所以建議,對於這兩個容器,優先使用成員函數版本的算法而不是通用算法。
- 鏈表類型還定義了splice算法。
- 鏈表版本會改變底層容器。
10.42
list1.sort();
list1.unique();
2/26/2019 7:41:55 PM
C++ Primer 第十章 泛型算法 筆記