algorithm頭文件中定義了大多數算法。
這些算法並不直接操作容器,而是遍歷由兩個迭代器指定的一個元素範圍,對其中的每個元素進行一些處理。
迭代器令算法不依賴於容器類型,但是算法依賴於元素類型的操作。大多數算法都使用了一個(或多個)元素類型上的操作,因此,除了默認操作符(==,<,>)外,我們還可以使用自定義的操作來代替默認操作。
注意:泛型算法本身不會執行容器的操作,它們只會運行於迭代器之上,執行迭代器的操作。
一、泛型算法分類
理解算法最基本的方法就是了解它們是否讀取元素、改變元素或是重排元素順序。
1、只讀算法
只會讀取其輸入範圍內的元素,而從不改變元素的算法稱爲只讀算法。
查找特定元素:
find(vec.cbegin(), vec.cend(), val);
數特定元素出現的次數:
count(vec.cbegin(), vec.cend(), val);
範圍內元素求和:
accumulate(vec.cbegin(), vec.cend(), 0);//第三個參數爲和的初值
比較兩個序列是否相等:
accumulate(vec.cbegin(), vec.cend(), 0);//第三個參數爲和的初值
2、寫容器元素的算法
寫容器元素算法可以將新值賦予序列中的元素。
給定首尾迭代器,向容器範圍內寫元素:
fill(vec.begin(), vec.end(), val);//將vec容器中所以元素置爲val
給定首迭代器和要寫入的元素數,向容器範圍內寫元素:
fill_n(vec.begin(), n, val);//將vec.begin()開始的n個元素置爲val
給定首尾迭代器,拷貝算法:
copy(roster1.cbegin(), roster1.cend(), roster2.begin());//將roster1中的所有元素拷貝到roster2.begin()開始的位置
給定首迭代器和要拷貝的元素數,拷貝算法:
copy_n(roster1.cbegin(), n, roster2.begin());//將roster1中roster1.cbegin()開始的n個元素拷貝到roster2.begin()開始的位置
替代算法:
replace(ilist.begin(), ilist.end(), 0, 42);//將ilist中所有的0元素替換爲42
替代拷貝算法:
replace_copy(ilist.cbegin(), lilist.cend(), back_inster(ivec), 0, 42);//在ivec中插入ilist的一份拷貝,但其中所有的0元素都被替換爲42
3、重排容器元素的算法
利用元素類型<運算符進行排序的算法:
清除相鄰重複項的算法:
將sort和unique結合使用可以實現序列清除重複項的排序。
二、定製操作
對於要比較元素的算法,默認使用的是<或==運算符。但在一些特殊場合,需要自己定義操作來代替默認運算符。
1、向算法傳遞函數——謂詞
謂詞是一個可調用的表達式,其返回結果是一個能用作條件的值。
例子:接受一個二元謂詞參數的sort版本用這個謂詞代替<來比較元素。我們可以自定義sort的排序規則,比如按元素長度進行排序。
首先定義一個函數,它輸入兩個string對象,返回一個布爾量來表示這兩個string對象的長度關係。
bool isShorter (const string &s1, const string &s2)
{
return s1.size() < s2.size();
}
這個isShorter函數就可以作爲謂詞,在sort的一個3輸入參數的重載函數中使用。
sort(words.begin(), words.end(), isShorter);//由短到長排序words
2、lambda表達式
a)、問題的引出
帶謂詞的查找函數:
find_if(words.cbegin(), words.end(), isFinded);
由於find_if接受一元謂詞,也就是說我們傳遞給find_if的任何函數都必須嚴格接受一個參數,以便能用來自輸入序列的一個元素(而不是一個元素和一個長度)調用它。
但事實上,一個isFinded函數的實現需要兩個參數,一個string和一個長度。因此,isFinded函數不能成爲一元謂詞而在find_if函數中作爲判斷條件使用。
b)、使用lambda表達式
除了函數,任何“可調用對象”都可以作爲謂詞使用。對於一個對象或一個表達式,如果可以對其使用調用運算符,則稱其爲可調用的。
C++中共存在4種可調用對象:函數、函數指針、重載了函數調用運算符的類、lambda表達式。
一個lamdba表達式表示一個可調用的代碼單元,我們可以將其理解爲一個未命名的內聯函數。
形式:[capture list](paramater list) -> return type{function body}
其中,capture list是一個lambda所在函數中定義的局部變量的列表;paramater list是參數列表;return type表示返回值類型;function body是函數體。
注意:lambda必須使用尾置返回來指定返回類型。
我們可以忽略參數列表和返回類型,但必須永遠包括捕獲列表和函數體:
auto f = [] { return 42; };
c)、向lambda傳遞參數
與普通函數類似,lambda表達式的實參和形參必須匹配。
與普通函數不同,lambda表達式不能有默認參數,也就是說,一個lambda調用的實參數目永遠與形參數目相等。
例子:
與isShorter函數功能類似的lambda:
[](const string &a, const string &b)
{ return a.size() < b.size();}
我們可以在stable_sort中調用該lambda代替來isShorter函數:
stable_sort(words.begin(), word.end(),
[](const string &a, const string &b)
{ return a.size() < b.size();});
d)、使用捕獲列表
對於a)中提出的問題,怎樣構造一個表達式,可以將輸入序列中每個string長度與給定的長度進行比較?
事實上,我們可以使用捕獲列表將這個給定的長度(設爲sz)捕獲,作爲lambda表達式的一部分。
[sz](const string &a)
{return a.size() >= sz;};
使用此lambda,我們就可以查找第一個長度大於等於sz的元素:
auto wc = find_if(word.begin(), words.end(),
[sz] (const string &a)
{ return a.size() >= sz;});
注意:調用find_if的函數必須具有形參sz,否則lambda將無法捕獲sz,造成錯誤。